到目前为止,已经简单学习了Spring的Core模块、....于是我们就开启了Spring的AOP模块了...在讲解AOP模块之前,首先我们来讲解一下 cglib代理、以及怎么手动实现AOP编程
在讲解cglib之前,首先我们来回顾一下静态代理和动态代理....我之前就写过了静态代理、动态代理的博文: http://blog.csdn.net/hon_3y/article/details/70655966
由于静态代理需要实现目标对象的相同接口,那么可能会导致代理类会非常非常多....不好维护---->因此出现了动态代理
动态代理也有个约束: 目标对象一定是要有接口的,没有接口就不能实现动态代理 .....----->因此出现了cglib代理
cglib代理也叫子类代理, 从内存中构建出一个子类来扩展目标对象的功能!
接下来我们就讲讲怎么写cglib代理:
//需要实现MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor{
    
    // 维护目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }
    
    // 给目标对象创建代理对象
    public Object getProxyInstance(){
        //1. 工具类
        Enhancer en = new Enhancer();
        //2. 设置父类
        en.setSuperclass(target.getClass());
        //3. 设置回调函数
        en.setCallback(this);
        //4. 创建子类(代理对象)
        return en.create();
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        
        System.out.println("开始事务.....");
        
        // 执行目标对象的方法
        Object returnValue = method.invoke(target, args);
        
        System.out.println("提交事务.....");
        
        return returnValue;
    }
} 
 public class App {
    public static void main(String[] args) {
        UserDao userDao = new UserDao();
        UserDao factory = (UserDao) new ProxyFactory(userDao).getProxyInstance();
        factory.save();
    }
} 
   
  
  
  
使用cglib就是为了弥补动态代理的不足【动态代理的目标对象一定要实现接口】
AOP 面向切面的编程:
下面我们来看一段代码:
// 保存一个用户
public void add(User user) { 
        Session session = null; 
        Transaction trans = null; 
        try { 
            session = HibernateSessionFactoryUtils.getSession();   // 【关注点代码】
            trans = session.beginTransaction();    // 【关注点代码】
             
            session.save(user);     // 核心业务代码
             
            trans.commit();     //…【关注点代码】
        } catch (Exception e) {     
            e.printStackTrace(); 
            if(trans != null){ 
                trans.rollback();   //..【关注点代码】
            } 
        } finally{ 
            HibernateSessionFactoryUtils.closeSession(session);   ////..【关注点代码】
        } 
   } 
 public interface IUser {
    void save();
} 
 我们一步一步来分析, 首先我们的UserDao有一个save()方法,每次都要开启事务和关闭事务
//@Component  -->任何地方都能用这个
@Repository  //-->这个在Dao层中使用
    public class UserDao {
    public void save() {
        System.out.println("开始事务");
        System.out.println("DB:保存用户");
        System.out.println("关闭事务");
    }
} 
 //@Component  -->任何地方都能用这个
@Repository  //-->这个在Dao层中使用
    public class UserDao {
    public void save() {
        begin();
        System.out.println("DB:保存用户");
        close();
        
    }
    public void begin() {
        System.out.println("开始事务");
    }
    public void close() {
        System.out.println("关闭事务");
    }
} 
 public class AOP {
    
    public void begin() {
        System.out.println("开始事务");
    }
    public void close() {
        System.out.println("关闭事务");
    }
} 
 @Repository  //-->这个在Dao层中使用
public class UserDao {
    AOP aop;
    public void save() {
        aop.begin();
        System.out.println("DB:保存用户");
        aop.close();
    }
    
} 
 public class ProxyFactory {
    //维护目标对象
    private static Object target;
    //维护关键点代码的类
    private static AOP aop;
    public static Object getProxyInstance(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 returnValue = method.invoke(target, args);
                        aop.close();
                        return returnValue;
                    }
                }
        );
    }
} 
 //把该对象加入到容器中
@Component
public class AOP {
    public void begin() {
        System.out.println("开始事务");
    }
    public void close() {
        System.out.println("关闭事务");
    }
} 
 @Component
public class UserDao {
    public void save() {
        System.out.println("DB:保存用户");
    }
} 
 <?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:p="http://www.springframework.org/schema/p"
       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/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <bean id="proxy" class="aa.ProxyFactory" factory-method="getProxyInstance">
        <constructor-arg index="0" ref="userDao"/>
        <constructor-arg index="1" ref="AOP"/>
    </bean>
    <context:component-scan base-package="aa"/>
</beans> 
 public class App {
    public static void main(String[] args) {
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("aa/applicationContext.xml");
        IUser iUser = (IUser) ac.getBean("proxy");
        iUser.save();
    }
} 
   
  
上面使用的是工厂静态方法来创建代理类对象。我们也 使用一下非静态的工厂方法创建对象 。
package aa;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * Created by ozc on 2017/5/11.
 */
public class ProxyFactory {
    public Object getProxyInstance(final Object target_, final 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 returnValue = method.invoke(target_, args);
                        aop_.close();
                        return returnValue;
                    }
                }
        );
    }
} 
 配置文件: 先创建工厂,再创建代理类对象
<?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:p="http://www.springframework.org/schema/p"
       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/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--创建工厂-->
    <bean id="factory" class="aa.ProxyFactory"/>
    <!--通过工厂创建代理-->
    <bean id="IUser" class="aa.IUser" factory-bean="factory" factory-method="getProxyInstance">
        <constructor-arg index="0" ref="userDao"/>
        <constructor-arg index="1" ref="AOP"/>
    </bean>
    <context:component-scan base-package="aa"/>
</beans> 
   
  
Aop: aspect object programming 面向切面编程
关注点:
// 保存一个用户
public void add(User user) { 
        Session session = null; 
        Transaction trans = null; 
        try { 
            session = HibernateSessionFactoryUtils.getSession();   // 【关注点代码】
            trans = session.beginTransaction();    // 【关注点代码】
             
            session.save(user);     // 核心业务代码
             
            trans.commit();     //…【关注点代码】
        } catch (Exception e) {     
            e.printStackTrace(); 
            if(trans != null){ 
                trans.rollback();   //..【关注点代码】
            } 
        } finally{ 
            HibernateSessionFactoryUtils.closeSession(session);   ////..【关注点代码】
        } 
   } 
 切面:
public class AOP {
    public void begin() {
        System.out.println("开始事务");
    }
    public void close() {
        System.out.println("关闭事务");
    }
} 
 切入点:
切入点表达式:
1) 先引入aop相关jar文件 (aspectj aop优秀组件)
注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题 。
2) bean.xml中引入aop名称空间
xmlns:context="http://www.springframework.org/schema/context"  http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd  引入4个jar包:
  
  
<?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:p="http://www.springframework.org/schema/p"
       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/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    
</beans> 
 我们之前手动的实现AOP编程是需要自己来编写代理工厂的 ,现在有了Spring,就不需要我们自己写代理工厂了。Spring内部会帮我们创建代理工厂 。
因此,我们 只要关心切面类、切入点、编写切入表达式指定拦截什么方法就可以了!
还是以上一个例子为案例,使用Spring的注解方式来实现AOP编程
<?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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="aa"/>
    <!-- 开启aop注解方式 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans> 
 @Component
@Aspect//指定为切面类
public class AOP {
    //里面的值为切入点表达式
    @Before("execution(* aa.*.*(..))")
    public void begin() {
        System.out.println("开始事务");
    }
    @After("execution(* aa.*.*(..))")
    public void close() {
        System.out.println("关闭事务");
    }
} 
 @Component
public class UserDao implements IUser {
    @Override
    public void save() {
        System.out.println("DB:保存用户");
    }
} 
 public interface IUser {
    void save();
} 
 public class App {
    public static void main(String[] args) {
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("aa/applicationContext.xml");
        //这里得到的是代理对象....
        IUser iUser = (IUser) ac.getBean("userDao");
        System.out.println(iUser.getClass());
        iUser.save();
        
    }
} 
   
  
上面我们测试的是UserDao有IUser接口,内部使用的是动态代理...那么我们这次测试的是目标对象没有接口
@Component
public class OrderDao {
    public void save() {
        System.out.println("我已经进货了!!!");
        
    }
} 
 public class App {
    public static void main(String[] args) {
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("aa/applicationContext.xml");
        OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
        System.out.println(orderDao.getClass());
        orderDao.save();
    }
} 
   
  
    
    // 前置通知 : 在执行目标方法之前执行
    @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()");
    }
    
    // 环绕通知:环绕目标方式执行
    @Around("pointCut_()")
    public void around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕前....");
        pjp.proceed();  // 执行目标方法
        System.out.println("环绕后....");
    } 
 我们的代码是这样的: 每次写Before、After等,都要重写一次切入点表达式,这样就不优雅了。
    @Before("execution(* aa.*.*(..))")
    public void begin() {
        System.out.println("开始事务");
    }
    @After("execution(* aa.*.*(..))")
    public void close() {
        System.out.println("关闭事务");
    } 
 于是乎,我们要 使用@Pointcut这个注解,来指定切入点表达式,在用到的地方中,直接引用就行了!
@Component
@Aspect//指定为切面类
public class AOP {
    // 指定切入点表达式,拦截哪个类的哪些方法
    @Pointcut("execution(* aa.*.*(..))")
    public void pt() {
    }
    @Before("pt()")
    public void begin() {
        System.out.println("开始事务");
    }
    @After("pt()")
    public void close() {
        System.out.println("关闭事务");
    }
} 
 首先,我们把所有的注解都去掉...
<?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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--对象实例-->
    <bean id="userDao" class="aa.UserDao"/>
    <bean id="orderDao" class="aa.OrderDao"/>
    <!--切面类-->
    <bean id="aop" class="aa.AOP"/>
    <!--AOP配置-->
    <aop:config >
        <!--定义切入表达式,拦截哪些方法-->
        <aop:pointcut id="pointCut" expression="execution(* aa.*.*(..))"/>
        <!--指定切面类是哪个-->
        <aop:aspect ref="aop">
            <!--指定来拦截的时候执行切面类的哪些方法-->
            <aop:before method="begin" pointcut-ref="pointCut"/>
            <aop:after method="close" pointcut-ref="pointCut"/>
        </aop:aspect>
    </aop:config>
    
</beans> 
 public class App {
    @Test
    public  void test1() {
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("aa/applicationContext.xml");
        OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
        System.out.println(orderDao.getClass());
        orderDao.save();
    }
    @Test
    public  void test2() {
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("aa/applicationContext.xml");
        IUser userDao = (IUser) ac.getBean("userDao");
        System.out.println(userDao.getClass());
        userDao.save();
    }
} 
 测试OrderDao
  
  
测试UserDao
  
  
切入点表达式主要就是来 配置拦截哪些类的哪些方法
..我们去文档中找找它的语法...
  
  
在文档中搜索:execution(
  
  
那么它的语法是这样子的:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
符号讲解:
参数讲解:
官方也有给出一些例子给我们理解:
  
  
        <!-- 【拦截所有public方法】 -->
        <!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
        
        <!-- 【拦截所有save开头的方法 】 -->
        <!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
        
        <!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 -->
        <!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
        
        <!-- 【拦截指定类的所有方法】 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
        
        <!-- 【拦截指定包,以及其自包下所有类的所有方法】 -->
        <!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
        
        <!-- 【多个表达式】 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!-- 下面2个且关系的,没有意义 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        
        <!-- 【取非值】 -->
        <!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--> 
 如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以 关注微信公众号:Java3y