1、Spring的AOP编程

   什么是AOP?  ----- 在软件行业AOP为Aspect Oriented Programming  也就是面向切面编程,使用AOP编程的好处就是:在不修改源代码的情况下,可以实现代码功能的增强

   AOP的实现原理(掌握)

    JDK的动态代理(注意JDK的动态代理只能对实现了接口的类产生代理)

/**

 * Jdk的动态代理

 * @author lilong

 */

public class JdkProxy implements InvocationHandler{

 

   //要代理的对象

   private CustomerDao customerDao;

  

   public JdkProxy(CustomerDao customerDao){

      this.customerDao = customerDao;

   }

   /**

    * 生成代理对象的方法

    * @return

    */

   public CustomerDao createProxy(){

      CustomerDao proxy = (CustomerDao) Proxy.newProxyInstance(customerDao.getClass().getClassLoader(), customerDao.getClass().getInterfaces(), this);

      return proxy;

   }

 

   /**

    * 增强的方法

    */

   @Override

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      System.out.println("权限校验...");

      return method.invoke(customerDao, args);

   }

}

 

编写测试代码:

@Test

public void test1(){

      CustomerDao customerDao = new CustomerDaoImpl();

      JdkProxy jdkProxy = new JdkProxy(customerDao);

      CustomerDao proxy = jdkProxy.createProxy();

      proxy.save();

}

 

第二种代理方式Cglib方式(了解或者欣赏)

注意的是:Cglib可以对没有实现接口的类产生代理,生成子类来实现功能的增强

public class CglibProxy implements MethodInterceptor{

 

   //要代理的对象

   private LinkManDao linkManDao;

  

   public CglibProxy(LinkManDao linkManDao) {

      this.linkManDao = linkManDao;

   }

  

   public LinkManDao createProxy(){

      //创建Cglib核心类

      Enhancer enhancer = new Enhancer();

      //设置父类

      enhancer.setSuperclass(linkManDao.getClass());

      //设置回调

      enhancer.setCallback(this);

      //生成代理

      LinkManDao proxy = (LinkManDao) enhancer.create();

      return proxy;

   }

 

   @Override

   public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

      System.out.println("日志记录");

      Object obj = methodProxy.invokeSuper(proxy, args);

      return obj;

   }

}

n  编写测试代码

@Test

public void test2(){

      LinkManDao linkManDao = new LinkManDao();

      CglibProxy cglibProxy = new CglibProxy(linkManDao);

      LinkManDao proxy = cglibProxy.createProxy();

      proxy.save();

}

 

 

2、Spring的AOP开发方式(xml方式)

其中相关术语的介绍

 

 

AOP开发所需要的jar介绍

n  AOP联盟的jar包:com.springsource.org.aopalliance-1.0.0.jar

n  Spring提供的AOP的jar包:spring-aop-4.2.4.RELEASE.jar

n  AspectJ的jar包:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

n  Spring整合AspectJ的jar包:spring-aspects-4.2.4.RELEASE.jar

 编写接口和实现类

ProductDao接口:

public interface ProductDao {

 

   /**

    * 持久层:产品保存

    */

   public void save();

  

}

ProductDaoImpl实现类:

public class ProductDaoImpl implements ProductDao {

 

   @Override

   public void save() {

      System.out.println("持久层:产品保存...");

   }

 

}

  配置相关类到Spring中

<?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: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="productDao" class="cn.itcast.dao.impl.ProductDaoImpl"></bean>

   

</beans>

  编写切面类

/**

 * 自定义切面类

 * @author kevin

 */

public class MyAspectXml {

 

   public void checkPrivilege(){

      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: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="productDao" class="cn.itcast.dao.impl.ProductDaoImpl"></bean>

  <bean id="myAspectXml" class="cn.itcast.aspect.MyAspectXml"></bean>

</beans>

 进行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: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="productDao" class="cn.itcast.dao.impl.ProductDaoImpl"></bean>

  <bean id="myAspectXml" class="cn.itcast.aspect.MyAspectXml"></bean>

  <!-- AOP配置 -->

  <aop:config>

      <!-- 配置切入点 -->

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>

      <!-- 配置切面 -->

      <aop:aspect ref="myAspectXml">

         <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>

      </aop:aspect>

  </aop:config>

</beans>

  编写测试类

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:applicationContext.xml")

public class TestAOP {

  

   @Autowired

   private ProductDao productDao;

 

   @Test

   public void test1(){

      productDao.save();

   }

  

}

 总结:对切入点表达式语法进行总结

语法:[修饰符] 返回类型 包名.类名.方法名(形式参数)

常见写法:

n  execution(public * *(..))                                                                                      所有的public方法

n  execution(* set(..))                                                                                               所有set开头的方法

execution(* com.xyz.service.AccountService.*(..))                              AccountService类中的所有方法

execution(* com.xyz.service.*.*(..))                                                              com.xyz.service包下所有的方法

n  execution(* com.xyz.service..*.*(..))                                                           com.xyz.service包及其子包下所有的方法

 

3、Spring中AOP的通知类型

1.1.1.   前置通知:在方法执行之前增强。可以获得切入点信息。

<!-- AOP配置 -->

<aop:config>

      <!-- 配置切入点 -->

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>

      <!-- 配置切面 -->

      <aop:aspect ref="myAspectXml">

         <!-- 前置通知 -->

         <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>

      </aop:aspect>

</aop:config>

 

public class MyAspectXml {

 

   public void checkPrivilege(JoinPoint point){

      System.out.println("权限校验..." + point);

   }

 

}

 

1.1.2.   后置通知:在方法执行完之后增强。可以获取返回值信息。

/**

 * 自定义切面类

 * @author kevin

 */

public class MyAspectXml {

 

   public void checkPrivilege(JoinPoint point){

      System.out.println("权限校验..." + point);

   }

  

   public void afterReturn(Object result){

      System.out.println("后置通知:" + result);

   }

}

 

public class ProductDaoImpl implements ProductDao {

 

   @Override

   public void save() {

      System.out.println("持久层:产品保存...");

   }

  

   public int delete(){

      System.out.println("持久层:产品删除...");

      return 100;

   }

 

}

 

   <!-- AOP配置 -->

  <aop:config>

      <!-- 配置切入点 -->

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.delete(..))" id="pointcut2"/>

      <!-- 配置切面 -->

      <aop:aspect ref="myAspectXml">

         <!-- 前置通知 -->

         <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>

         <!-- 后置通知 -->

         <aop:after-returning method="afterReturn" pointcut-ref="pointcut2" returning="result"/>

      </aop:aspect>

  </aop:config>

 

 

1.1.3.   环绕通知:在方法执行前后都进行增强。可以阻止方法的执行。

public class ProductDaoImpl implements ProductDao {

 

   @Override

   public void save() {

      System.out.println("持久层:产品保存...");

   }

  

   public int delete(){

      System.out.println("持久层:产品删除...");

      return 100;

   }

 

   @Override

   public void update() {

      System.out.println("持久层:产品更新");

   }

 

}

 

public class MyAspectXml {

 

   public void checkPrivilege(JoinPoint point){

      System.out.println("权限校验..." + point);

   }

  

   public void afterReturn(Object result){

      System.out.println("后置通知:" + result);

   }

  

   public Object around(ProceedingJoinPoint joinpoint){

      System.out.println("环绕前执行");

      Object obj = null;

      try {

          obj = joinpoint.proceed();

      } catch (Throwable e) {

         e.printStackTrace();

      }

      System.out.println("环绕后执行");

      return obj;

   }

}

 

<!-- AOP配置 -->

<aop:config>

      <!-- 配置切入点 -->

     <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.delete(..))" id="pointcut2"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.update(..))" id="pointcut3"/>

      <!-- 配置切面 -->

      <aop:aspect ref="myAspectXml">

         <!-- 前置通知 -->

         <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>

         <!-- 后置通知 -->

         <aop:after-returning method="afterReturn" pointcut-ref="pointcut2" returning="result"/>

         <!-- 环绕通知 -->

         <aop:around method="around" pointcut-ref="pointcut3"/>

      </aop:aspect>

</aop:config>

 

 

1.1.4.   异常抛出通知:当发生异常之后增强,可以获取异常信息。

public class ProductDaoImpl implements ProductDao {

 

   @Override

   public void save() {

      System.out.println("持久层:产品保存...");

   }

  

   public int delete(){

      System.out.println("持久层:产品删除...");

      return 100;

   }

 

   @Override

   public void update() {

      System.out.println("持久层:产品更新");

   }

 

   @Override

   public void find() {

      System.out.println("持久层:查询");

      int i = 10/0;

   }

 

}

 

public class MyAspectXml {

 

   public void checkPrivilege(JoinPoint point){

      System.out.println("权限校验..." + point);

   }

  

   public void afterReturn(Object result){

      System.out.println("后置通知:" + result);

   }

  

   public Object around(ProceedingJoinPoint joinpoint){

      System.out.println("环绕前执行");

      Object obj = null;

      try {

          obj = joinpoint.proceed();

      } catch (Throwable e) {

         e.printStackTrace();

      }

      System.out.println("环绕后执行");

      return obj;

   }

  

   public void afterThrowing(Exception ex){

      System.out.println("抛出异常通知:" + ex.getMessage());

   }

}

 

 

<!-- AOP配置 -->

 <aop:config>

      <!-- 配置切入点 -->

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.delete(..))" id="pointcut2"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.update(..))" id="pointcut3"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.find(..))" id="pointcut4"/>

      <!-- 配置切面 -->

      <aop:aspect ref="myAspectXml">

         <!-- 前置通知 -->

         <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>

         <!-- 后置通知 -->

         <aop:after-returning method="afterReturn" pointcut-ref="pointcut2" returning="result"/>

         <!-- 环绕通知 -->

         <aop:around method="around" pointcut-ref="pointcut3"/>

         <!-- 抛出异常通知 -->

         <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>

      </aop:aspect>

</aop:config>

 

1.1.5.   最终通知:不管是否有异常,都会执行的

public class MyAspectXml {

 

   public void checkPrivilege(JoinPoint point){

      System.out.println("权限校验..." + point);

   }

  

   public void afterReturn(Object result){

      System.out.println("后置通知:" + result);

   }

  

   public Object around(ProceedingJoinPoint joinpoint){

      System.out.println("环绕前执行");

      Object obj = null;

      try {

          obj = joinpoint.proceed();

      } catch (Throwable e) {

         e.printStackTrace();

      }

      System.out.println("环绕后执行");

      return obj;

   }

  

   public void afterThrowing(Exception ex){

      System.out.println("抛出异常通知:" + ex.getMessage());

   }

  

   public void after(){

      System.out.println("最终通知");

   }

}

 

<!-- AOP配置 -->

  <aop:config>

      <!-- 配置切入点 -->

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.delete(..))" id="pointcut2"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.update(..))" id="pointcut3"/>

      <aop:pointcut expression="execution(* cn.itcast.dao.impl.ProductDaoImpl.find(..))" id="pointcut4"/>

      <!-- 配置切面 -->

      <aop:aspect ref="myAspectXml">

         <!-- 前置通知 -->

         <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>

         <!-- 后置通知 -->

         <aop:after-returning method="afterReturn" pointcut-ref="pointcut2" returning="result"/>

         <!-- 环绕通知 -->

         <aop:around method="around" pointcut-ref="pointcut3"/>

         <!-- 抛出异常通知 -->

         <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>

         <!-- 最终通知 -->

         <aop:after method="after" pointcut-ref="pointcut4"/>

      </aop:aspect>

  </aop:config>

注意:最终通知和后置通知的区别:最终通知,不管异常与否,都执行;而后置通知在异常时不执行。

 

 

4、Spring的AOP注解

4.1. 创建工程,引入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: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="cn.itcast"></context:component-scan>

        <!-- 开启自动代理注解 -->

       <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

       

</beans>

4.2. 创建接口和实现类

public interface ProductDao {

 

   /**

    * 持久层:产品保存

    */

   public void save();

  

   public int delete();

  

   public void update();

  

   public void find();

  

}

 

@Repository("productDao")

public class ProductDaoImpl implements ProductDao {

 

   @Override

   public void save() {

      System.out.println("持久层:产品保存...");

   }

  

   public int delete(){

      System.out.println("持久层:产品删除...");

      return 100;

   }

 

   @Override

   public void update() {

      System.out.println("持久层:产品更新");

   }

 

   @Override

   public void find() {

      System.out.println("持久层:查询");

   }

 

}

4.3. 编写切面类

@Component("myAspectAnnotation")

@Aspect

public class MyAspect {

 

   @Before("execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))")

   public void checkPrivilege(JoinPoint joinPoint){

      System.out.println("权限校验..." + joinPoint.toString());

   }

 

}

提示:此处的切面类可以不取id.

5、Spring的AOP 中注解通知

 

5.1. 前置通知

/**

    * 前置通知

    * @param joinPoint

    */

   @Before("execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))")

   public void checkPrivilege(JoinPoint joinPoint){

      System.out.println("权限校验..." + joinPoint.toString());

   }

5.2. 后置通知

@Aspect

public class MyAspect {

 

/**

    * 前置通知

    * @param joinPoint

    */

   @Before("execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))")

   public void checkPrivilege(JoinPoint joinPoint){

      System.out.println("权限校验..." + joinPoint.toString());

   }

 

  

   @AfterReturning(value="execution(* cn.itcast.dao.impl.ProductDaoImpl.delete(..))",returning="result")

   public void afterReturning(Object result){

      System.out.println("后置通知:" + result);

   }

  

  

}

5.3. 环绕通知

@Around("execution(* cn.itcast.dao.impl.ProductDaoImpl.update(..))")

public Object after(ProceedingJoinPoint joinpoint) throws Throwable{

      System.out.println("环绕通知前增强");

      Object obj = joinpoint.proceed();

      System.out.println("环绕通知后增强");

      return obj;

}

5.4. 异常通知

@AfterThrowing(value="execution(* cn.itcast.dao.impl.ProductDaoImpl.find(..))",throwing="ex")

public void afterThrowing(Exception ex){

      System.out.println("抛出异常通知");

}

 

5.5. 最终通知

@After("execution(* cn.itcast.dao.impl.ProductDaoImpl.find(..))")

public void after(){

      System.out.println("最终通知");

}

5.6. PointCut注解(了解)

作用:用于定义切入点表达式的一个注解。

@Component("myAspectAnnotation")

@Aspect

public class MyAspect {

 

   /**

    * 前置通知

    * @param joinPoint

    */

   @Before("execution(* cn.itcast.dao.impl.ProductDaoImpl.save(..))")

   public void checkPrivilege(JoinPoint joinPoint){

      System.out.println("权限校验..." + joinPoint.toString());

   }

  

   @AfterReturning(value="execution(* cn.itcast.dao.impl.ProductDaoImpl.delete(..))",returning="result")

   public void afterReturning(Object result){

      System.out.println("后置通知:" + result);

   }

  

   @Around("execution(* cn.itcast.dao.impl.ProductDaoImpl.update(..))")

   public Object aroung(ProceedingJoinPoint joinpoint) throws Throwable{

      System.out.println("环绕通知前增强");

      Object obj = joinpoint.proceed();

      System.out.println("环绕通知后增强");

      return obj;

   }

  

// @AfterThrowing(value="execution(* cn.itcast.dao.impl.ProductDaoImpl.find(..))",throwing="ex")

   @AfterThrowing(value="MyAspect.pointcut()",throwing="ex")

   public void afterThrowing(Exception ex){

      System.out.println("抛出异常通知");

   }

  

// @After("execution(* cn.itcast.dao.impl.ProductDaoImpl.find(..))")

   @After("MyAspect.pointcut()")

   public void after(){

      System.out.println("最终通知");

   }

  

   @Pointcut("execution(* cn.itcast.dao.impl.ProductDaoImpl.find(..))")

   public void pointcut(){

     

   }

  

}