场景:针对非业务代码但是又不得不做的,不可能针对每个模块都编写一次。
解决:
如开启和关闭事务
第一种实现:
public class UserServiceImpl implements UserService { @Override public void updateUser(User user) { beginTransaction(); System.out.println(user); endTransaction(); } @Override public void addUser(User user) { beginTransaction(); System.out.println(user); endTransaction(); } public void beginTransaction(){ System.out.println("开启事务!"); } public void endTransaction(){ System.out.println("关闭事务!"); } }第二种实现
public class TransactionHandler { public void beginTransaction(){ System.out.println("开启事务!"); } public void endTransaction(){ System.out.println("关闭事务!"); } } public class UserServiceImplHandler extends TransactionHandler implements UserService { @Override public void addUser(User user) { this.beginTransaction(); System.out.println(user); this.endTransaction(); } @Override public void updateUser(User user) { this.beginTransaction(); System.out.println(user); this.endTransaction(); } }第三种:动态代理的方式
动态代理区分两种
JDK动态代理
代理对象和目标对象必需实现相同的接口
package com.fxyh.spring.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class TransactionHandlerWithJDKDynamicProxy implements InvocationHandler { private Object targetObject; public TransactionHandlerWithJDKDynamicProxy() { } public TransactionHandlerWithJDKDynamicProxy(Object targetObject) { this.targetObject = targetObject; } public Object createProxyInstance(){ return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } /** * * @param proxy 代理对象 * @param method 目标对象的方法 * @param args 方法的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object returnObj; if (proxy == null) throw new IllegalArgumentException(""); if (method == null) throw new IllegalArgumentException(""); String methodName = method.getName(); boolean flag = methodName.startsWith("add") || methodName.startsWith("delete") || methodName.startsWith("update"); if (flag){ beginTransaction(); } returnObj = method.invoke(this.targetObject,args); if (flag){ endTransaction(); } return returnObj; } private void endTransaction() { System.out.println("JDK关闭事务!"); } private void beginTransaction() { System.out.println("JDK开启事务!"); } } package com.fxyh.spring.service.impl; import com.fxyh.spring.entity.User; import com.fxyh.spring.proxy.TransactionHandlerWithCGlibDynamicProxy; import com.fxyh.spring.service.UserService; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class UserServiceImplHandlerWithCGlibDynamicProxyTest { private UserService userService; private TransactionHandlerWithCGlibDynamicProxy transactionHandlerWithCGlibDynamicProxy; @Before public void setUp() { this.transactionHandlerWithCGlibDynamicProxy = new TransactionHandlerWithCGlibDynamicProxy(new UserServiceImplHandlerWithCGlibDynamicProxy()); this.userService = (UserService) this.transactionHandlerWithCGlibDynamicProxy.createProxyInstance(); } @Test public void addUser() { User user = new User(); user.setUsername("zhangsan"); user.setPassword("123456"); this.userService.addUser(user); } }Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this);
这也是为啥jdk动态代理要实现相同的接口的原因。
CGLib动态代理
代理对象和目标对象不需要实现相同的接口,只需要维护父子关系
package com.fxyh.spring.proxy; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.InvocationHandler; import java.lang.reflect.Method; public class TransactionHandlerWithCGlibDynamicProxy implements InvocationHandler { private Object targetObject; public TransactionHandlerWithCGlibDynamicProxy() { } public TransactionHandlerWithCGlibDynamicProxy(Object targetObject) { this.targetObject = targetObject; } public Object createProxyInstance(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.targetObject.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object returnObj; if (proxy == null) throw new IllegalArgumentException(""); if (method == null) throw new IllegalArgumentException(""); String methodName = method.getName(); boolean flag = methodName.startsWith("add") || methodName.startsWith("delete") || methodName.startsWith("update"); if (flag){ beginTransaction(); } returnObj = method.invoke(this.targetObject,args); if (flag){ endTransaction(); } return returnObj; } private void endTransaction() { System.out.println("CGlib关闭事务!"); } private void beginTransaction() { System.out.println("CGlib开启事务!"); } } package com.fxyh.spring.service.impl; import com.fxyh.spring.entity.User; import com.fxyh.spring.proxy.TransactionHandlerWithCGlibDynamicProxy; import com.fxyh.spring.service.UserService; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class UserServiceImplHandlerWithCGlibDynamicProxyTest { private UserService userService; private TransactionHandlerWithCGlibDynamicProxy transactionHandlerWithCGlibDynamicProxy; @Before public void setUp() { this.transactionHandlerWithCGlibDynamicProxy = new TransactionHandlerWithCGlibDynamicProxy(new UserServiceImplHandlerWithCGlibDynamicProxy()); this.userService = (UserService) this.transactionHandlerWithCGlibDynamicProxy.createProxyInstance(); } @Test public void addUser() { User user = new User(); user.setUsername("zhangsan"); user.setPassword("123456"); this.userService.addUser(user); } @Test public void updateUser() { } }这里要注意要导入spring-aop和spring-aspects两个包。
这里定义的只有add和delete开头的方法才会开启和关闭事务。findUserById测试的时候就不会开启事务。
配置:
xml方式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.fxyh.spring"/> <aop:aspectj-autoproxy /> </beans>JavaConfig方式
package com.fxyh.spring; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @ComponentScan("com.fxyh.spring") @EnableAspectJAutoProxy public class AppAOPConfig { }测试:
package com.fxyh.spring.service; import com.fxyh.spring.domain.User; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserServiceAutoAOPTest { private ApplicationContext context; private UserService userService; @Before public void setUp() throws Exception { //xml配置方式 this.context = new ClassPathXmlApplicationContext("classpath:applicationContext-autoAop.xml"); //JavaConfig方式 //this.context = new AnnotationConfigApplicationContext(AppAOPConfig.class); this.userService = this.context.getBean(UserService.class); } @Test public void saveUser() { User user = new User(); user.setId(1); user.setUsername("zhangsan"); user.setAge(18); userService.saveUser(user); } @Test public void findUserById() { userService.findUserById(1); } }需要完整的源码可以去我的GitHub下载:https://github.com/fxyh9712/SpringStudy