首先我们准备一下用于演示的代码:以模拟MVC模式的经典架构来解释
pojo层
@Repository
public void User{
private String name;
}
Dao层接口以及实现类
public interface UserDao{
public void saveUser(User user);
}
@Component
public class UserDaoImpl implements UserDao{
@Override
public void saveUser(User user){
System.out.println("保存了一个用户:"+user);
}
}
Service层接口以及实现类
public interface Uservice{
public void saveUser(User user;
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
public void saveUser(User user){
userDao.saveUser(user);
}
}
Servlet 层
@Controller
public class UserServlet{
@Autowired
private UserServcie userService;
public void saveUser(User user){
userService.saveUser(user);
}
}
事务类
@Component
public class TxManager{
public void begin(){
System.out.println("开启了事务");
}
public void commit(){
System.out.println("提交了事务");
}
}
一、静态代理
要求代理类和被代理的类有相同的方法,因此我们在创建代理类的时候要和被代理的类实现相同的接口。
要求将事务处理横向放入service层而且做到神不知鬼不觉
@Service("userService")
public class StaitcProxy{
//在代理类中添加目标对象,因为要执行目标类中的方法(也就是被代理类中的方法)
@Autowired
@Qulifier("userServiceImpl")
private UserService target;
//添加事务代码
@Autowired
private TxManager txmanager;
@Override
public void saveUser(User user){
txmanager.begin();
target.saveUser(user);
txmanager.commit();
}
}
测试类
pulbic class Mytest{
@Test
public void Test(){
ApplicationContext context=ClassPathXmlApplicationContext("applicationContext.xml");
User user = context.getBean(User.class);
UserServlet userServlet = context.getBean(UserServlet.class);
userServlet.saveUser(user);
}
}
输出的结果是
开启了事务
保存了一个用户:user=null;
提交了事务
有上述模拟测试可知:静态代理降低了耦合度,但是在如果每一个方法都需要事务,就需要创建很多的代理类,代码复用性大大降低。因此出现了动态代理:首先模拟一下JDK提供的动态代理:
1首先我们需要创建一个动态的代理类,并创建获取被代理的类对象的方法
public class JDkDynamicProxy{
private TxManager txmanager;
//构造方法中为事务对象赋值
public JDKDynamicProxy(){
txmanager = new TxManager;
}
//创建代理实例对象的方法
public Object getProxy(final Object target){
//创建一个代理实例对象 参数介绍:1.类加载器为了创建相同功能的类
//2.目标类的接口 3.回调参数 代理对象执行任何方法的时候会回调
Object proxyObject=Proxy.newProxyInstance(target.getClass().getClassLoader,target.getClass.getInterface,newInvocationHandler(){
@Override
public Object invoke(Object proxy,Method method,Object[] agrs) throws throwable{
txmanager.begin();
Object result = method.invoke(target,args);
txmanager.commit();
return result;
}
});
return proxy;
}
}
JDK提供的动态代理解决了复用性问题,需要添加事务的时候会自动创建代理类,执行相关的操作。那么新的问题又来了,如果一个类没有接口那么此代理便不能使用。这就引出了CGLib动态代理,这是由Spring框架提供的动态代理,接下来我们来模拟演示一下:
//创建一个动态代理类,声明一个获取代理对象的方法
public static Object getProxy(final Object target,final Txmanager txmanager){
//创建一个增强器
Enhancer enhancer = new Enhancer();
//通过增强器设置接口
enhance.setInterface(target.getClass().getInterfaces());
//通过增强器获取父类
enhance.setSuperclass(target.getClass());
//设置回调
enhance.setCallback(new MethodInterceptor(){
@Override
public Object interceptor(Object proxy,Method method,Object[] args,MthodProxy arg3){
txmanaget.begin();
Object result = method.invoke(target,args);
txmanager.commit();
return result;
}
});
return enhance.create();
}
以上通过代理进行横向植入并不能进行对被代理对象方法的过滤,就比如将事务植入service层,service层中所有方法都要被植入事务,但是需求并不可能都要求如此。这时Spring框架中AOP就可以弥补这种缺陷。接下来我们来演示一下:
//创建事务切面类和通知方法
@Component
pulic class TxAspect{
//添加事务对象
@Autowired
private TxManager txmanger;
//通知方法 我们用环绕通知,这样可以控制目标类方法的执行。
public void around(ProceedingJoinPoint joinPoint){
txmanager.begin();
//执行目标中的方法
txmanager.proceed();
txmanager.commit();
}
}
Spring核心配置文件:
<!--配置AOP-->
<aop:config>
<!--配置切入点-->
<aop:ponitcut expression="within(cn.tedu.sevice.*) id="pc"/>
<!--配置切面-->
<aop:aspect ref="txAspect">
<!--通知方法-->
<aop:around method="around" pointcut-ref="pc"/>
</aop:aspect
</aop:config>