spring学习
2021-08-01 13:56
标签:pointer 字符 out 持久层 int 起点 容器 循环 struct //把创建的对象都放到applicationContext容器中,要用时,在通过id一个一个取出来 壹、第一个spring项目 主配置文件 //在spring中,每一个java对象对应一个标签,即一个标签代表一个java对象 //id:对象的自定义名称,唯一值,spring通过这个名称找到该对象 //class:类的全限定名称(不能是接口,spring通过反射机制创建对象) //spring把创建好的对象放到map集合中,集合的key就是上面的id值,value是创建的对象 //spring创建对象默认调用无参构造方法 测试程序 String config="beans.xml";ApplicationContext ac=new ClassPathXmlApplicationContext(config); (2)?Object object=ac.getBean("a"); //此时对象已经创建好了,通过id,从容器中获取相对应的对象SomeService service=(SomeService)object;service.doSome(); //只要是配置文件,都是从target/classes的根目录为起点开始找的 //ApplicationContext就是spring的容器,在创建spring容器时,会创建配置文件中的所有java对象,也就是在(2)步时,先创建容器,再创建java对象 //ClassPathXmlApplicationContext(config):表示从类路径(即target/classes目录)中加载spring的配置文件; FileSystemXmlApplicationContext():表示从本地磁盘中加载spring的配置文件,不常用 贰、常用的方法 获取容器中定义的对象数量 int nums=ac.getBeanDefinitionCount();System.out.println(nums); 获取容器中定义的对象名称 String[] names=ac.getBeanDefinitionNames();for (String n:names){ System.out.println(n); //a} 叁、基于XML的DI //在spring的配置文件中,使用标签和属性完成赋值,叫做基于XML的DI实现 一、set注入 //set方法的执行是在构造方法之后执行的 简单注入 //name="属性名字",value="赋给属性的值" //com.dh.bao.Student类中所赋值的属性必须有set()方法,没有就报错;有set(),但里面没有this.age = age;赋值语句,不报错,只是该属性不能被赋值,为null //spring只关心有没有对应的set()方法,即使只有set()方法,没有属性,系统也会正常运行 //若在set()方法中有System.out.println(123);语句,该语句也会正常运行,输出123 引用类型注入 ? //com.dh.bao.School类中有Student属性,使用ref属性完成赋值 //两个标签的位置没有规定,谁在上方都可以。因为在第一次因位置没加载到时,会进行二次扫描 二、构造注入 //通过有参构造方法赋值 三、引用类型的自动注入 byName ? //student的中的id值必须和com.dh.bao.School类中的Student类型的引用名称相同 //在School的中添加autowire="byName"属性 byType //同源关系 java类中引用属性的数据类型和该引用属性相关的中的class是一样的 java类中引用属性的数据类型和该引用属性相关的中的class是父子关系 java类中引用属性的数据类型和该引用属性相关的中的class是接口与实现类关系 //在spring的配置文件中也是由上而下逐行执行 //当执行到School的标签时,先创建school对象,然后再给其简单属性赋值。最后执行autowire="byType"语句;扫描整个spring配置文件,找与School对象的属性student同源的,即student的子类,student类本身,若student是接口,其实现类也行。找到之后赋值即可 //注意:在使用byType时,同时只能有一个符合条件,当有多个标签符合条件时,系统报错 肆、基于注解的DI 一、创建对象 Component注解 @Component(value="myStudent")public class Student { private String name; private int age; ...} //当只有value一个属性时,value可以省略不写;也可以不指定对象的名称,Spring默认提供类名首字母小写为对象名称 配置文件中设置组件扫描器 //spring会扫描base-package属性指定的包和其子包中所有的类,找到类中的注解,按照注解的功能创建对象或者赋值 二、多注解的分层 @Repository:用在持久层上,放在dao的实现类上,创建dao对象 @Service:用在业务层上,创建service对象,可以有事务等功能 @Controller:用在界面层上,创建控制器对象 //以上3个注解的使用语法与@Component一样,都能创建对象,但这3个注解还有其他的额外功能 //当某个类不属于以上3种情况时,才用@Component创建对象 三、简单类型赋值 @Component(value="myStudent")public class Student { @Value(value = "zs") private String name; @Value(value="29") private int age; ...} //属性value是String类型 //@Value注解可以出现在属性的定义上(推荐),也可以出现在set()方法上 //@Value注解是通过反射机制赋值的,所以该类可以没有set()方法 四、引用类型赋值 @Autowired注解 @Component(value="mySchool")public class School { @Value("donghua") private String name; @Value("shanghai") private String address; @Autowired private Student student; ...}?@Component(value="myStudent")public class Student { @Value(value = "zs") private String name; @Value(value="29") private int age; ...} //@Autowired注解默认采用的是byType的方式自动注入 //只要通过组件扫描器扫描的包中有student属性对应的同源的关系的类就能完成该引用属性的赋值;或者spring配置文件有通过标签完成对该引用同源关系的类赋值的,也是可以的。即两种方式可以混用,即xml与注解 //@Autowired注解可以出现在属性的定义上(推荐),也可以出现在set()方法上。其是通过反射机制赋值,所以该类可以没有set()方法 1. byName(@Qualifier) @Component(value="mySchool")public class School { @Value("donghua") private String name; @Value("shanghai") private String address; @Autowired @Qualifier(value="aa") //@Qualifier注解 private Student student; ...}?@Component(value="aa")public class Student { @Value(value = "zs") private String name; @Value(value="29") private int age; ...} //使用@Qualifier注解指定赋值的类的id值 2. required属性 @Autowired(required = true) //required = true:表示引用类型赋值失败时,程序报错,并终止接下来的程序 (默认) //required = false:表示引用类型赋值失败时,程序正常执行,引用属性的值为null @Resource注解 @Component(value="mySchool")public class School { @Value("donghua") private String name; @Value("shanghai") private String address; @Resource private Student student; ...} //@Resource注解来自jdk中,但spring框架支持该注解的功能,用法与@Autowired注解相似 //@Resource注解默认采用byName的方式,其先使用byName的方式进行赋值,若赋值失败,则采用byType的方式再次尝试 //若只想使用byName的方式,则需添加属性name:@Resource(name="aa") //@Resource注解可以出现在属性的定义上(推荐),也可以出现在set()方法上。其是通过反射机制赋值,所以该类可以没有set()方法 伍、多个配置文件 //在resources/bao目录下有spring-1,spring-2,spring-3,3个配置文件 //在spring-1中添加 //classpath与target/classes是等价的 //添加以上内容之后,spring-1就是主配置文件了,在测试程序中,只用加载spring-1,其他两个配置文件就会自动加载 //可以使用通配符“ * ” //这样就可以一次将spring-2,spring-3都导入。但要注意,在通配符表示的范围中不能包含主配置文件spring-1,不然会陷入死循环,一直加载。可以通过给主配置文件改名或者修改目录,不在同一目录下 陆、AOP //aop (Aspect Orient Programming) :面向切面编程。切面:给目标类增加的功能,就是切面 //aop的底层是基于动态代理的,它的的本质就是动态代理的规范化,是一个标准 //spring内部实现了aop规范,但其主要用于事务的处理。我们主要用一个专门做aop的框架:aspectJ //所以aop支持jdk代理和cglib代理 一、切面表达式 execution (访问权限 方法的返回类型 方法的声明(参数) 异常) //方法的返回类型,方法的声明(参数)这两项是必须要写的,其他两项可以省略不写 //每一部分之间用空格隔开 //切入表达式中可以用以下符号 *:0至多个字符 ..:用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包的路径 +:用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类 //例子:excution(* set*(..)) :表示以set三个字母开头的任意方法 二、第一个例子 创建接口及目标类 package com.dh.bao;?public interface SomeService { void doSome(String name, Integer age);}?public class SomeServiceImpl implements SomeService{ //目标类 public void doSome(String name, Integer age) { System.out.println("abc"); }} 创建切面类,切面表达式指向目标类 @Aspectpublic class MyAspect { @Before(value = "execution(public void com.dh.bao.SomeServiceImpl.doSome(String,Integer))") public void MyBefore(){ System.out.println("切面:" + new Date()); }} 声明目标及切面对象,还有声明自动代理生成器 ? //自动代理生成器 //程序执行到 时,会扫描整个spring的配置文件中所有的类及对象,找到@Aspect及相关注解,将切面类指向的目标类转化为代理类 测试程序 String confg="applicationContext.xml";ApplicationContext ac=new ClassPathXmlApplicationContext(confg);SomeService ss=(SomeService)ac.getBean("someservice"); //这里强转的是接口,而不是接口的实现类SomeServiceImpls.doSome("zs",20); ?//切面:Thu Jun 24 10:59:54 CST 2021 abc //注意:此时ss对象的类型不是SomeService类,而是com.sun.proxy.$Proxy6类型 //若切面表达式写错了,程序依然会正常运行,不会报错;只是ss对象就不是代理类型,而是SomeService类型。原因是切面表达式不对,导致指定目标类错误,找不到目标类,也就无法完成转换 三、注解 @Aspect注解 //作用是:表明当前类为切面类 //位置:定义在类上 @Pointcut注解 //定义和管理切入点,若项目中有多个切入点表达式是重复的,则可以使用该注解,达到复用的目的 属性:value值(切入点表达式) 位置:在方法上 特点:当使用@Pointcut注解定义在一个方法上时,这个方法的名称就是切入表达式的别名。在其他的通知注解中,value属性的值就可以使用这个别名,代替切入点表达式 @Pointcut(value = "execution(public Integer com.dh.arround.MyArroundImpl.doSome())")public void www(){ //无需写代码} //注意:在其他通知注解中,value="www()",而不是value="www"。是要包含括号的 四、通知 //切面类相对于目标类执行的时间,在aop规范中称为Advice,也就是通知。 JoinPoint参数 //指定通知方法中的参数,即切面方法中的参数;所有通知方法都可以有这个参数 //通知方法:被通知注解(@Before,@After,@AfterReturning等)修饰的方法 public class MyAspect { @Before(value = "execution(public void com.dh.bao.SomeServiceImpl.doSome(String,Integer))") public void MyBefore(JoinPoint jp){ System.out.println("目标方法的签名:" + jp.getSignature()); System.out.println("目标方法的名称" + jp.getSignature().getName()); Object args[]=jp.getArgs(); //获取目标方法的实参 ...} //目标方法的签名:void com.dh.bao.SomeServiceImpl.doSome(String,Integer) @Before (前置通知注解) 属性:value值(切入点表达式) 位置:在方法上 特点:在目标类的方法之前执行;不改变目标方法的执行结果;不影响目标方法的执行 对切面方法的要求:要是public;要是void;方法名称自定义;方法若有参数,不能自定义,从几个参数类型中选择 @AfterReturning (后置通知) 属性:value值(切入点表达式); returning:自定义变量,表示目标方法的返回值(自定义的变量名必须和通知(切面)方法的形参名一样) 位置:在方法上 特点:在目标类的方法之后执行; 能够获取到目标方法的返回值,可以在切面方法中处理这个返回值 不能改变目标方法的返回值在最后的输出 对切面方法的要求:要是public;要是void;方法名称自定义;方法有参数,推荐Object类型 public class AfterImpl implements After{ public Integer doSome() { return 100; }}?public class AfterAspect { @AfterReturning(value = "execution(public Integer com.dh.After.AfterImpl.doSome())",returning = "bbb") public void myAfter(Object bbb){ System.out.println("切面:" + bbbb); //由获取的返回值可以输出一些额外的功能,但不能改变目标返回值的最后输出 bbb=200; }}?After after=(After)ac.getBean("afterimpl");Integer in=after.doSome();System.out.println(in); //100,并没有变成200 @Around (环绕通知注解) 属性:value值(切入点表达式) 位置:在方法上 特点: 在目标类的方法前后都能执行 能控制目标方法是否被调用执行 修改原来目标方法的执行结果,影响最后的调用结果 对切面方法的要求: public; 必须有一个返回值,推荐使用Object; 方法名称自定义; 方法有参数,固定参数:ProceedingJoinPoint;其是JoinPoint接口的子接口,所以它能用JoinPoint中的所有方法 public class MyArroundImpl implements MyArround{ public Integer doSome() { System.out.println("目标"); return 1; }}?public class MyArroundAspect { @Around(value = "execution(public Integer com.dh.arround.MyArroundImpl.doSome())") public Object myArround(ProceedingJoinPoint pjp) throws Throwable { Object result=null; System.out.println("目标前"); (1) result=pjp.proceed(); (2) System.out.println("目标后"); (3) return result; }} //可在(1)处添加pjp.getArgs()方法,获取目标方法的实参,可修改实参,也可通过实参判断是否继续执行目标方法,即(2) //(2)为执行目标方法,获取目标方法的返回值 //在(3)处可对目标方法的返回值进行修改,这是会影响最后在测试程序中的输出结果的。与@AfterReturning中的不同 @AfterThrowing (异常通知) 属性:value值(切入点表达式); throwing:自定义变量,表示目标方法抛出的异常(自定义的变量名必须和通知(切面)方法的形参名一样) 位置:在方法上 特点:在目标类方法抛出异常时执行; 可以做异常的监控程序,监控目标方法执行时是不是有异常 对切面方法的要求:要是public;要是void;方法名称自定义;参数只有一个:Exception,若还有就是JoinPoint //常用的方法: System.out.println(ex.getMessage()); //打印异常信息 @After (最终通知注解) 属性:value值(切入点表达式) 位置:在方法上 特点:在目标方法之后执行 总是会执行,即使目标方法出现异常,也会执行(相当于try...catch中的finally) 所以一般用来做资源的关闭,清理工作 对切面方法的要求:要是public;要是void;方法名称自定义;方法没有参数,若有就是JoinPoint 五、cglib动态代理 //若目标类有接口时,使用的jdk代理。被转换的目标类的类型是:com.sun.proxy.$Proxy6 //若目标程序没有接口,spring框架就自动使用cglib代理,不需要程序员操作;被转换的目标类的类型是: com.dh.arround.MyArroundImpl$$EnhancerBySpringCGLIB$$8fc79603 //若目标类有接口,但你希望用cglib代理,则将自动代理生成器改为: //使用cglib代理的前提是:目标类能被继承 柒、spring与mybatis整合 spring配置文件分析 ? //通过连接数据源aaa和连接mybatis配置文件,在内部先创建SqlSessionFactory对象,然后再由该对象创建SqlSession对象 //因为是标签,则com.alibaba.druid.pool.DruidDataSource类中一定有url属性对应的set()方法,因为该标签是通过set注入完成赋值的 //MapperScannerConfigurer类会在内部调用getMapper()方法,生成每个dao接口的代理对象 //MapperScannerConfigurer会扫描basePackage指定的包中所有的接口,把每个接口都执行一次getMapper()方法,得到每个接口的dao对象,再把创建好的dao对象放进spring容器中 //dao对象的默认名称:接口名首字母小写 //此时给StudentServiceImpl类中的studentDao属性赋值的就是上面那步生成的dao对象 测试程序分析 String conf="applicationContext.xml";ApplicationContext ac=new ClassPathXmlApplicationContext(conf);StudentService ss=(StudentService)ac.getBean("ccc");?Student student=new Student();student.setId(7);student.setName("gg");student.setAge(44);student.setSex(0);int nums=ss.addStudent(student);List list=ss.queryStudent();for(Student stu:list){ System.out.println(stu);} //注意:此时的ss对象是com.dh.service.impl.StudentServiceImpl,而不是com.sun.proxy.$Proxy类型,在aop中才是 //spring与mybatis整合中常通过在service中声明dao对象,并给其赋值,再用其在service类中调用dao接口中的方法;而不是直接使用dao对象调用dao接口中的方法,中间要转一下 //spring与mybatis整合在一起使用时,事务是自动提交的,无需手动写SqlSession.commit() 捌、spring中的事务 //不同的数据库访问技术处理事务的方式不同,比如:jdbc(conn.commit())与mybatis(SqlSession.commit())处理事务的方式就不同,所以spring就提供了一种事务处理的统一模型,能用统一的步骤,方式完成多种不同数据库访问技术的事务处理 一、事务管理器 //即PlatformTransactionManager接口,封装了commit,rollback等方法 //该接口为每一种数据访问技术都设置好了专门的实现类,比如mybatis对应的实现类是DataSourceTransactionManager 二、事务定义接口 //即TransactionDefinition接口 //该接口中定义了事务描述相关的三类常量:事务的隔离级别,事务的传播行为,事务默认的超时时限 事务的隔离级别 隔离级别含义ISOLATION_DEFAULT 默认, 使用后端数据库默认的隔离级别 ISOLATION_READ_UNCOMMITTED 读未提交, 允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。 ISOLATION_READ_COMMITTED 读已提交,允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。(Oracle 默认级别) ISOLATION_REPEATABLE_READ 可重复读, 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。(MYSQL默认级别) ISOLATION_SERIALIZABLE 串行化, 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。 事务的传播行为 //只用掌握前面3种传播行为即可 传播行为含义PROPAGATION_REQUIRED 指定的方法必须在事务内运行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新的事务。这是spring默认的传播行为 PROPAGATION_SUPPORTS 表示指定的当前方法可以在没有事务上下文中执行;但是如果存在当前事务的话,那么该方法会在这个事务中运行。查询操作就属于这种行为 PROPAGATION_REQUIRES_NEW 指定的方法总是会新建一个事务,并在新建的事务中执行,若当前存在事务,则将当前事务挂起(即暂停,包括事务中所有的方法也暂停),直到新事务执行完毕 //PROPAGATION_REQUIRED实例解释:PROPAGATION_REQUIRED传播行为指定方法a,若B事务中的b方法调用方法a,则a方法加入到B事务中执行;若没有事务的b方法调用a方法。则a方法自己新建一个事务c,在事务c中执行 传播行为含义PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常 PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务 事务默认的超时时限 //为了避免一个事务长时间占有资源,所以设置了超时时间 //表示一个方法或事务的最长执行时间,如果方法执行时间超过了这个超时时限,事务就会回滚。单位是:秒,整数值,默认值为:-1 三、提交事务与回滚事务时机 //当你的业务方法,执行成功,没有异常抛出,spring会在方法执行完毕,自动调用事务管理器中的commit方法,提交事务 //在默认设置下,事务只在出现运行时异常(Runtimeexception及其子类)或者错误(Error)时回滚事务,而在出现非运行异常时,主要是受检查异常(checked exception)时不回滚,自动调用事务管理器中的commit方法提交事务。 //不过可以人为设置,主动声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。 四、事务处理方案 @Transactional注解 //适用于中小型项目 //该注解使用的是spring框架中内部的aop来处理事务 //@Transactional注解只能用于public方法,用在非public方法时,Spring虽然不会报错,但不会将该方法纳入该事物中,因为spring会忽略所有非public方法上的@Transactional注解 属性 propagation:事务传播行为设置,该属性类型为Propagation枚举(7个),默认值为:Propagation_REQUIRED isolation:事务隔离级别设置,该属性类型为Isolation枚举(5个),默认值为Isolation_DEFAULT readOnly:设置目标方法对数据库的操作是否只能读事务,而不能读写事务。该属性为boolean,默认读写,即为false timeout:事务超时时间设置。单位:秒,类型为:int,默认值为:-1,即没有时限 rollbackFor:指定需要回滚的异常类(即出现该指定异常类后,该事物回滚,即使该异常属于非运行时异常)。类型为Class[],默认值为空数组,当只有一个异常类时,可以不使用数组。 rollbackForClassName:指定需要回滚的异常类的类名,类型为String[],默认值为空数组,当只有一个异常类时,可以不使用数组。 noRollbackFor:指定不需要回滚的异常类(即出现该指定异常类后,该事物不回滚,即使该异常属于运行时异常)。类型为Class[],默认值为空数组,当只有一个异常类时,可以不使用数组。 noRollbackForClassName:指定不需要回滚的异常类的类名,类型为String[],默认值为空数组,当只有一个异常类时,可以不使用数组。 value :可选的限定描述符,指定使用的事务管理器 底层原理 //spring使用aop机制,创建该注解所在类的代理对象,给方法加入事务功能。使用了环绕通知的方式,在目标业务方法执行之前,在切面方法中开启事务,在业务方法结束之后,提交事务,或者出现异常时,回滚事务,这一切都有spring框架自动控制。 使用步骤 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, //这些配置都是默认的,可以不写这些属性,直接@Transactional在方法上 readOnly = false ......)public void buy(Integer goodsId, Integer nums) { ...} //声明事务管理器并注册,是对数据源声明事物管理器 //ref属性:其值为数据源的id值,在这里是阿里的那个数据源 //:是对事务管理器进行注册,创建代理对象 aspectj //适合大型项目 //使用aspectj框架功能在spring的配置文件中声明类,方法的事务。这种方式业务方法与事务配置是完全分离的 //使用该方式需要在提前加入aspectj的依赖 声明事务管理器 声明业务方法的业务属性 //name:方法名称(不包含包名和类名),可以使用完整方法名,也可以用通配符 “*” 。当你需要给成千上万的方法配置事务时,你可以给同一类的方法命名时有共同点,比如:以字母abc开头,则在name属性处填:abc*,就能给这一类的方法添加事务;当name=“*”,表示给目标类中所有方法都添加该事物 //当有一方法同时满足完整方法名,带有*的方法名,以及只有*,即这三种情况同时存在时,spring会先匹配完成方法名,当匹配成功后,就不再匹配其他的;只有匹配失败,再去带有*的方法名中找,前两者都没匹配成功,才执行只有*的事务配置 配置aop //:配置切入点表达式,指定哪些包中的类需要使用业务(即目标业务方法所在的包名,类名) //:配置增强器,关联advice和pointcut(连接目标类和业务方法)spring学习标签:pointer 字符 out 持久层 int 起点 容器 循环 struct 原文地址:https://www.cnblogs.com/zhestudy-2021/p/14961117.html
上一篇:window.name
下一篇:Jmeter之HTTP请求默认值