笔者自学的框架,知识点来自网上学习教程,方便实惠,推荐,特此声明。
spring
spring中的ioc反向代理
-
ApplicationContext接口(推荐)
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //从spring中获取对象 StudentService studentService = (StudentService) context.getBean("studentService"); studentService.study();
-
BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(new FileSystemResource("F:\\java\\ssm\\learnspring\\src\\main\\resources\\applicationContext.xml"));
-
bean的三种实例方式
3.1 第一种为默认方法,通过实现bean的接口实现类来实例化<bean id="studentService" class="com.java.service.impl.StudentServiceImpl"/>
3.2 以实例工厂模式返回bean的实现类
<bean id="myFactory" class="com.java.factory.MyBeanFactory"/> <bean id="studentService" factory-bean="myFactory" factory-method="createStudentService"/>
3.3 以静态工厂返回bean的实现类
<bean id="studentService" class="com.java.factory.MyBeanFactory" factory-method="createStudentService"/>
-
bean的生命周期和作用域
4.1 作用域:设定bean的scope属性,有以下几种- singleton: 单态模式。即在一个Spring ioc容器中,使用 singleton 定义的 Bean 是单例的,只有一个实例。默认为单例的。
- prototype: 原型模式。即每次使用 getBean 方法获取的同一个bean的实例都是一个新的实例。
- request:对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。
- session:对于每个不同的 HTTP session,都将产生一个不同的 Bean 实例。
- application:在一个web应用中会产生一个bean实例,就相当于在一个ServletContext中只有该bean的实例。
- websocket:在一个websocket中会产生一个bean实例。
4.2 bean的初始化和销毁
<bean id="studentService" init-method="init" destroy-method="destroy" class="com.java.service.impl.StudentServiceImpl"/>
4.3bean初始化的前后操作
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("执行before"); //这里要将bean返回 return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ("studentService".equals(beanName)) { //创建InvocationHandler对象 InvocationHandler invocationHandler = ((Object p, Method method, Object[] args) -> { //调用study方法时使用动态代理对其进行增强 if ("study".equals(method.getName())) { System.out.println("======目标方法开始======="); //执行目标方法 Object result = method.invoke(bean, args); System.out.println("======目标方法结束======="); return result; } return method.invoke(bean, args); }); //增强bean Object proxy = Proxy.newProxyInstance( bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), invocationHandler ); System.out.println("postProcessAfterInitialization执行"); return proxy;//把增强的代理类返回 } return bean; } }
依赖注入DI
- xml(可通过autowire="byName"属性设置注入方式,黄体部分可修改)
1.1 构造方式
1.2 setter方式<bean id="userService" class="com.monkey1024.service.impl.UserServiceImpl"> <constructor-arg name="userDao" ref="userDaoId"/> </bean> <bean id="userDaoId" class="com.monkey1024.dao.impl.UserDaoImpl"/>
1.3 注入集合<bean id="userService" class="com.monkey1024.service.impl.UserServiceImpl"> <property name="userDao" ref="userDaoId"/> </bean> <bean id="userDaoId" class="com.monkey1024.dao.impl.UserDaoImpl"/>
<!-- Definition for javaCollection --> <bean id="javaCollection" class="com.tutorialspoint.JavaCollection"> <!-- results in a setAddressList(java.util.List) call --> <property name="addressList"> <list> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>USA</value> </list> </property> <!-- results in a setAddressSet(java.util.Set) call --> <property name="addressSet"> <set> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>USA</value> </set> </property> <!-- results in a setAddressMap(java.util.Map) call --> <property name="addressMap"> <map> <entry key="1" value="INDIA"/> <entry key="2" value="Pakistan"/> <entry key="3" value="USA"/> <entry key="4" value="USA"/> </map> </property> <!-- results in a setAddressProp(java.util.Properties) call --> <property name="addressProp"> <props> <prop key="one">INDIA</prop> <prop key="two">Pakistan</prop> <prop key="three">USA</prop> <prop key="four">USA</prop> </props> </property> </bean> </beans>
- 注解
扫描包:
2.1注册bean<!--文件扫描器--> <context:component-scan base-package="com.monkey1024"/>
@Component:
@Repository 用于对 DAO 实现类进行注解
@Service 用于对 Service 实现类进行注解
@Controller 用于对 Controller 实现类进行注解
2.2注入bean
spring提供@Autowired注解(可用@Qualifier设置id)
jdk提供@Resource注解,作用同上
@Scope注解:指定bean的作用域
spring读取多个xml文件
- 方式一,在主程序中读取多个文件
String[] files = {"spring-aop.xml","spring-bean.xml","applicationContext.xml"}; ApplicationContext context = new ClassPathXmlApplicationContext(files);
- 方式二 将xml配置文件导入到主xml中
<import resource="spring-aop.xml"/> <import resource="spring-bean.xml"/> <import resource="spring-*.xml"/>
AOP
-
术语介绍
(1)目标对象(Target)
目标对象指 将要被增强的对象。即包含主业务逻辑的类的对象。上例中的UserDaoImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。(2)切面(Aspect)
切面泛指非业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面有通知,实际就是对业务逻辑的一种增强。(3)连接点(JoinPoint)
连接点指可以被切面织入的方法。通常业务接口中的方法均为连接点。(4)切入点(Pointcut)
切入点指切面具体织入的方法。在 UserDaoImpl 类中,若 addUser()被增强,而doOther()不被增强,则 addUser()为切入点,而 doOther()仅为连接点。 被标记为 final 的方法是不能作为连接点与切入点的,因为是不能被修改的,不能被增强的。(5)通知(Advice)
通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。上例中的MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。Advice有下面几种,这里使用常用的AspectJ方式:- 前置通知(Before advice):在连接点之前执行,即目标方法执行之前执行。
- 后置通知(After returning advice):在连接点正常结束之后执行,如果连接点抛出异常,则不执行。
- 异常通知(After throwing advice):在连接点抛出异常后执行
- 最终通知(After (finally) advice):在连接点结束之后执行,无论是否抛出异常,都会执行。
- 环绕通知(Around advice):在连接点之前和之后均执行。
(6)织入(Weaving)
织入是指将切面代码插入到目标对象的过程。上例中 MyInvocationHandler 类中的 invoke()
方法完成的工作,就可以称为织入。
(7)aop代理(AOP proxy)
spring中的aop代理有两种:jdk自带的动态代理和CGLIB代理。 -
xml使用AOP
添加依赖<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.4.RELEASE</version> </dependency>
bean配置(笔者学习的举例)
<!--注册bean--> <bean id="userService" class="com.monkey1024.service.impl.UserServiceImpl"/> <bean id="myAspect" class="com.monkey1024.aspect.MyAspect"/> <!--配置aop--> <aop:config> <!--定义切入点--> <aop:pointcut id="addUserPointcut" expression="execution(* com.monkey1024.service.impl.UserServiceImpl.addUser())"/> <aop:pointcut id="selectUserPointcut" expression="execution(* com.monkey1024.service.impl.UserServiceImpl.selectUser())"/> <aop:pointcut id="selectUserByIdPointcut" expression="execution(* com.monkey1024.service.impl.UserServiceImpl.selectUserById(..))"/> <aop:pointcut id="updateUserPointcut" expression="execution(* com.monkey1024.service.impl.UserServiceImpl.updateUser())"/> <aop:pointcut id="deleteUserPointcut" expression="execution(* com.monkey1024.service.impl.UserServiceImpl.deleteUser())"/> <!--定义切面--> <aop:aspect ref="myAspect"> <!--前置通知--> <aop:before method="before" pointcut-ref="addUserPointcut"/> <!--后置通知--> <aop:after-returning method="afterReturning" pointcut-ref="updateUserPointcut" returning="result"/> <!--异常通知--> <aop:after-throwing method="afterThrowing" pointcut-ref="selectUserByIdPointcut" throwing="e"/> <!--最终通知--> <aop:after method="after" pointcut-ref="selectUserPointcut"/> <!--环绕通知--> <aop:around method="around" pointcut-ref="deleteUserPointcut"/> </aop:aspect> </aop:config>
bean配置(spring官网举例)
<aop:config> <aop:aspect id="myAspect" ref="aBean"> <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/> <!-- a before advice definition --> <aop:before pointcut-ref="businessService" method="doRequiredTask"/> <!-- an after advice definition --> <aop:after pointcut-ref="businessService" method="doRequiredTask"/> <!-- an after-returning advice definition --> <!--The doRequiredTask method must have parameter named retVal --> <aop:after-returning pointcut-ref="businessService" returning="retVal" method="doRequiredTask"/> <!-- an after-throwing advice definition --> <!--The doRequiredTask method must have parameter named ex --> <aop:after-throwing pointcut-ref="businessService" throwing="ex" method="doRequiredTask"/> <!-- an around advice definition --> <aop:around pointcut-ref="businessService" method="doRequiredTask"/> ... </aop:aspect> </aop:config> <bean id="aBean" class="..."> ... </bean>
表达式
execution ( [modifiers-pattern] 访问权限类型 ret-type-pattern 返回值类型 [declaring-type-pattern] 全限定性类名 name-pattern(param-pattern) 方法名(参数名) [throws-pattern] 抛出异常类型 )
表达式举例
execution(public * *(..)) 指定切入点为:任意公共方法。 execution(* set*(..)) 指定切入点为:任何一个以“set”开始的方法。 execution(* com.xyz.service.*.*(..)) 指定切入点为:定义在 service 包里的任意类的任意方法。 execution(* com.xyz.service..*.*(..)) 指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟“*”,表示包、子包下的所有类。 execution(* *.service.*.*(..)) 指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点 execution(* *..service.*.*(..)) 指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点 execution(* *.ISomeService.*(..)) 指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点 execution(* *..ISomeService.*(..)) 指定所有包下的 ISomeSerivce 接口中所有方法为切入点 execution(* com.xyz.service.IAccountService.*(..)) 指定切入点为: IAccountService 接口中的任意方法。 execution(* com.xyz.service.IAccountService+.*(..)) 指定切入点为: IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。 execution(* joke(String,int))) 指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参 数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)。 execution(* joke(String,*))) 指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类型,如 joke(String s1,String s2)和 joke(String s1,double d2)都是,但 joke(String s1,double d2,String s3)不是。 execution(* joke(String,..))) 指定切入点为:所有的 joke()方法,该方法第 一个参数为 String,后面可以有任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(Strings1,double d2,String s3)都是。 execution(* joke(Object)) 指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。 execution(* joke(Object+))) 指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。
-
注解
3.1 添加依赖<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.4.RELEASE</version> </dependency>
3.2注册切面类bean
@Aspect @Component
3.3添加注解扫描以及aop注册<context:component-scan base-package="com.monkey1024"/> <!--配置AspectJ自动代理--> <aop:aspectj-autoproxy/>
3.4笔者学习举例
@Aspect @Component public class MyAspect { @Before("execution(* *..UserServiceImpl.addUser())") public void before() { System.out.println("========前置通知========"); } @After("execution(* *..UserServiceImpl.selectUser())") public void after() { System.out.println("========最终通知========:"); } @AfterThrowing(value = "execution(* *..UserServiceImpl.selectUserById(..))" ,throwing = "e") public void afterThrowing(Exception e) { System.out.println("========异常通知========:" + e); } @AfterReturning(value = "execution(* *..UserServiceImpl.updateUser())",returning = "result") public void afterReturning(int result) { System.out.println("========后置通知========:" + result); } @Around(value = "execution(* *..UserServiceImpl.deleteUser())") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("========环绕通知:前========:"); Object proceed = pjp.proceed(); System.out.println("========环绕通知:后========:"); return proceed; } }
3.5 官网举例
@Aspect public class Logging { /** Following is the definition for a pointcut to select * all the methods available. So advice will be called * for all the methods. */ @Pointcut("execution(* com.tutorialspoint.*.*(..))") private void selectAll(){} /** * This is the method which I would like to execute * before a selected method execution. */ @Before("selectAll()") public void beforeAdvice(){ System.out.println("Going to setup student profile."); } /** * This is the method which I would like to execute * after a selected method execution. */ @After("selectAll()") public void afterAdvice(){ System.out.println("Student profile has been setup."); } /** * This is the method which I would like to execute * when any method returns. */ @AfterReturning(pointcut = "selectAll()", returning="retVal") public void afterReturningAdvice(Object retVal){ System.out.println("Returning:" + retVal.toString() ); } /** * This is the method which I would like to execute * if there is an exception raised by any method. */ @AfterThrowing(pointcut = "selectAll()", throwing = "ex") public void AfterThrowingAdvice(IllegalArgumentException ex){ System.out.println("There has been an exception: " + ex.toString()); } } 下面是 Student.java 文件的内容: package com.tutorialspoint; public class Student { private Integer age; private String name; public void setAge(Integer age) { this.age = age; } public Integer getAge() { System.out.println("Age : " + age ); return age; } public void setName(String name) { this.name = name; } public String getName() { System.out.println("Name : " + name ); return name; } public void printThrowException(){ System.out.println("Exception raised"); throw new IllegalArgumentException(); } }
基于aop的事务管理
-
四个事务等级
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ;Oracle默认为 READ_COMMITTED。 READ_UNCOMMITTED:读未提交。未解决任何问题。 READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。 REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读 SERIALIZABLE:串行化。解决脏读、不可重复读,幻读的问题,效率低。
-
七种传播方式
REQUIRED 指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事 务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。 如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事 务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用 doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。 SUPPORTS 指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。 MANDATORY 指定的方法必须在当前事务内执行,若当前没有事务,则直接抛出异常。 REQUIRES_NEW 总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。 NOT_SUPPORTED 指定的方法不能在事务环境中执行,若当前存在事务,就将当前事务挂起。 NEVER 指定的方法不能在事务环境下执行,若当前存在事务,就直接抛出异常。 NESTED 指定的方法必须在事务内执行。若当前存在事务,则在嵌套事务内执行;若当前没有事务,则创建一个新事务。
-
xml方式
在spring-mybatis.xml文件中配置事务管理器<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> </bean>
配置事务管理
<!-- 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 传播行为 --> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="create*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> <tx:method name="select*" propagation="SUPPORTS" read-only="true" /> <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice>
配置要切入的service
<!-- 切面 --> <aop:config> <!--切入点必须是在service层--> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.monkey1024.service.*.*(..))" /> </aop:config> </beans>
-
使用注解配置事务管理器
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> </bean> <!--开启注解事务驱动--> <tx:annotation-driven transaction-manager="transactionManager"/>
然后使用@Transactional 注解即可,该注解可以用于类上,也可以用于方法上,需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。
@Transactional中的属性如下:
propagation :用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
isolation : 用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为Isolation.DEFAULT。
readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
rollbackForClassName: 指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。