之前找工作复习Java时, 又重新看来一篇Spring的六大模块, 包括之前不太明白的AOP模块, 现在来总结一下原理, 请注意这是我的理解,可以参考.
内容包括
主要内容包括: 一 代理 1) 代理模式 -- 静态代理 -- 动态代理(也叫JDK代理) -- Cglib代理 二 AOP 1)手动实现AOP编程 2)AOP编程 * 注解方式实现 3) Spring事务里面的AOP |
一 :代理模式
代理模式是Java23中设计模式之一, 如果想更加详细的了解设计模式,推荐查阅
书写风趣代理(Proxy)提供了对目标对象另外的访问方式;即通过代理访问目标对象。 这样好处: 可以在目标对象实现的基础上,增强额外的功能操作。(扩展目标对象的功能)。
举个例子: 生活中,秘书职位也是代理,你找到秘书,你的感觉是什么? 找到了老板.秘书把一些繁琐的工作做好,接着老板一看,准了,秘书再联系客户; 在这里秘书就是代理对象,老板就是目标对象!
1.1 静态代理,
该代理要求: 代理对象和目标对象必须实现相同的接口,
静态代理优缺点:
优点: 可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。
缺点:--》 因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。
--》 一旦接口增加方法,目标对象与代理对象都要维护。
注:其实不认为这是什么缺点.
实现如下:
//接口
package cn.itcast.scm.test.proxy;
public interface IuserService {
public void save();
}
//目标对象
package cn.itcast.scm.test.proxy;
public class UserService implements IuserService{
@Override
public void save() {
System.out.println("----数据保存成功----");
}
}
//代理对象
public class UserServiceProxy implements IuserService{
//目标对象
private IuserService target;
public UserServiceProxy(IuserService target) {
this.target=target;
}
@Override
public void save() {
System.out.println("开启事务");
target.save();
System.out.println("提交事务");
}
}
//测试一下
package cn.itcast.scm.test.proxy;
/**
* 测试代理模式
* */
public class TestProxy {
public static void main(String[] args) {
//静态代理
IuserService target=new UserService();
UserServiceProxy proxy=new UserServiceProxy(target);
proxy.save();
}
}
1.2 动态代理
也叫JDK代理, 该代理对目标对象的要求是: 必须实现接口。为什么? 可以思考!
package cn.itcast.scm.test.JDKproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工厂
* */
public class ProxyFactory {
//维护一个目标对象
private Object target;
public ProxyFactory( Object userserive) {
this.target=userserive;
}
//给目标对象,生成一个代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//可以判断方法;method
System.out.println("开启事务");
Object value=method.invoke(target, args);
System.out.println("提交事务");
return value;
}
});
}
}
1.3 cglib代理
分析:静态代理不足之处是:代理对象和目标对象必须实现一样的接口,动态代理则是目标对象必须实现接口,此时可使用cglib代理模式.
package it.cw.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibFactory implements MethodInterceptor {
//维护一个目标对象
private Object target;
public CglibFactory(Object target) {
this.target=target;
}
//生成代理对象
public Object getCglibInstant(){
//1) 代理工具类
Enhancer eh=new Enhancer();
//2)设置父类
eh.setSuperclass(target.getClass());
//3) 设置回调函数
eh.setCallback(this);
//4)返回代理对象
return eh.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] arg2,
MethodProxy proxy) throws Throwable {
System.out.println("开启事务");
Object object=method.invoke(target, arg2);
System.out.println("提交事务");
return object;
}
}
Cglib子类代理:
1) 需要引入cglib – jar文件, 但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core-3.2.5.jar即可。
2)引入功能包后,就可以在内存中动态构建子类
3)代理的类(目标对象)不能为final, 否则报错。 因为代理对象要继承它,
4)目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。
在Spring的AOP编程中,
如果加入容器的目标对象有实现接口,用JDK代理(动态代理);
如果目标对象没有实现接口,用Cglib代理;
***************************以上就是三种代理模式**********************************
了解了代理模式之后,接着就来理解AOP
二 AOP编程
2.1 好,我们来手动实现AOP,这是全手动实现,与springAOP无关!
首先在spring配置文件加上:用IOC容器创建代理对象;
Aop 切面类: 这是什么? 重复的代码叫关注点代码,组成的一个类就做切面类
/** 存放关注点代码, 即重复代码
*
* */
@Component
public class Aop {
public void begin(){
System.out.println("AOP begin");
}
public void end(){
System.out.println("end");
}
}
代理工厂 ProxyFactory
生成代理对象时: 二个参数, 目标对象和AOP对象
package it.cw.AOP.Design;
Import java.lang.reflect.InvocationHandler;
Import java.lang.reflect.Method;
Import java.lang.reflect.Proxy;
public class ProxyFactory {
private static Object target;
private static Aop aop;
//生成代理对象
public static Object getsProInstance(Object target_, Aop aop_){
target=target_;
aop=aop_;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
aop.begin();
Object obj=method.invoke(target, args);
aop.end();
return obj;
}
});
}
}
测试:
public class TestDao {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//测试手动AOP实现
@Test
public void test(){
IUserDao daoproxy= (IUserDao) ac.getBean("proxy");
daoproxy.save();
}
}
2.2 注解方式实现AOP
描述: 注解方式实现AOP编程, 便于理解Spring内置的AOP实现, 如”事务”处理机制, 如果需要我们应该自己去实现Aop给自己的业务使用; 了解几个概念;
Aop, aspect object programming 面向切面编程
功能: 让关注点代码与业务代码分离!
关注点,
重复代码就叫做关注点;
切面,
关注点形成的类,就叫切面(类)!
面向切面编程,就是指 对很多功能都有的重复的代码抽取,再在运行的时候网业务方法上动态植入“切面类代码”。
切入点,
执行目标对象方法,动态植入切面代码。
可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。
这不是全手动实现(我们实现AOP,SpringAOP自动生成代理对象), 这就需要得到spring的AOP支持,所以需要加上如下配置:
//名称空间和AOP注解支持
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
AOP切面类: 里面包括切入点,
/** 切面类, 重复代码构成
* */
@Component
@Aspect
public class AspectObject {
//切入点, 拦截那些方法
@Pointcut("execution(* cn.cw.AOP.announce.ServiceImpl.*(..))")
public void pointCut_(){
}
// 前置通知 : 在执行目标方法之前执行
@Before("pointCut_()")
public void begin(){
System.out.println("开始事务/异常");
}
// 后置/最终通知:在执行目标方法之后执行 【无论是否出现异常最终都会执行】
@After("pointCut_()")
public void after(){
System.out.println("提交事务/关闭");
}
// 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】
@AfterReturning("pointCut_()")
public void afterReturning() {
System.out.println("afterReturning()");
}
// 异常通知: 当目标方法执行异常时候执行此关注点代码
@AfterThrowing("pointCut_()")
public void afterThrowing(){
System.out.println("afterThrowing()");
}
// 环绕通知:环绕目标方式执行, 刚刚学习AOP,可以不看他
@Around("pointCut_()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕前....");
pjp.proceed(); // 执行目标方法
System.out.println("环绕后....");
}
}
public class TestAop {
@Test
public void test() {
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
Service se=(ServiceImpl) ac.getBean("service");
se.save();
}
}
在xml配置文件中创建service这个Bean时, 注意会自动创建代理对象, 传入哪二个参数? 我们写好的AOP, 和service目标对象
public class TestAop {
@Test
public void test() {
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
Service se=(ServiceImpl) ac.getBean("service");
se.save();
}
}
下面图解一下:
更明白了吧! 先想想SpringAOP对事物的处理是怎么样的!
2.3 Spring事务里面的AOP
和上面注解实现是一样的, 我们在事物中配置切入点时, 创建对象的类时,会先将切入点传入AOP(该类应该是内置存在的), 比如before要开启事务,after要关闭事物,一切都写明白了,在传入切入点参数,AOP就存在了, 在创建切入点指定的Service对象时, 将Service,AOP 作为参数生成代理对象, 我们就拿到了Service(看起来像老板,其实是秘书)的对象!
--- 到这里基本上就OK了, 我的理解是这样子的。