您的位置:首页 > 编程语言 > Java开发

spring的AOP--面向切面编程

2017-08-15 18:10 531 查看

AOP概念

1、AOP:aspect oriented programming 面向切面编程

2、AOP在spring中的作用

     提供声明式服务(声明式事务)

     允许用户实现自定义切面

3、AOP:在不改变原有代码的情况下,增加新的功能。

4、名词解释:

关注点:增加的某个业务。如日志、安全、缓存、事务、异常处理等。

        切   面 :一个关注点的模块化。

连接点:表示一个方法的执行。

通   知 :在切面的某个特定的连接点上执行的动作。

目标对象:被代理的对象--真实对象。

织   入 :把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象。

5、使用spring实现aop

第一种实现方式: 通过spring的api来实现

前置通知

项目结构如下:



查看spring中文帮助文档,文档下载地址:《spring中文帮助文档.chm

中有关于aop通知的介绍,我们现在使用前置通知,从文档中可以知道我们要实现MethodBeforeAdvice,如下图:



Log代码:

package cn.myspring.log;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

/**
* 日志切面
*/
public class Log implements MethodBeforeAdvice {

/**
* @param method
*            :被调用的方法对象
* @param args
*            : 被调用的方法的参数
* @param target
*            : 被调用方法的目标对象
*/
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName()
+ " 方法被执行");
}
}


UserService代码:

package cn.myspring.service;

/**
* 抽象角色
*/
public interface UserService {

public void add() ;
public void update() ;
public void delete() ;
public void search() ;
}


UserServiceImpl代码:

package cn.myspring.service;

/**
* 真实角色
*/
public class UserServiceImpl implements UserService {

@Override
public void add() {
System.out.println("增加用户");
}

@Override
public void update() {
System.out.println("修改用户");
}

@Override
public void delete() {
System.out.println("删除用户");
}

@Override
public void search() {
System.out.println("查询用户");
}

}


beans.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"
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"> 
<bean id="userService" class="cn.myspring.service.UserServiceImpl"></bean>
<bean id="log" class="cn.myspring.log.Log"></bean>
<aop:config>
<!-- aop:pointcut切入点配置:
execution() : 是表达式;
*  : 表示所有返回值 ;
位置 : cn.myspring.service.UserServiceImpl ;
方法 : add() ;
-->
<aop:pointcut expression="execution(* cn.myspring.service.UserServiceImpl.add())" id="pointcut"/>
<!-- aop:advisor : 告诉程序,切入点如嵌入的业务有哪些;
advice-ref  : 对应的公共业务 ;
pointcut-ref: 切入点。
-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
</beans>


Test类代码:

package com.myspring.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.myspring.service.UserService;

public class Test {

public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml") ;
UserService userService = (UserService) ac.getBean("userService") ;
userService.add() ;
}

}


运行Test类,控制台打印信息:

2017-8-16 10:04:28 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2747ee05: startup date [Wed Aug 16 10:04:28 CST 2017]; root of context hierarchy
2017-8-16 10:04:29 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans.xml]
2017-8-16 10:04:29 org.springframework.context.support.ClassPathXmlApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in class path resource [beans.xml]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0': Cannot resolve reference to bean 'pointcut' while setting bean property 'pointcut'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pointcut': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.aop.aspectj.AspectJExpressionPointcut]: No default constructor found; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in class path resource [beans.xml]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0': Cannot resolve reference to bean 'pointcut' while setting bean property 'pointcut'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pointcut': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.aop.aspectj.AspectJExpressionPointcut]: No default constructor found; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:479)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at com.myspring.test.Test.main(Test.java:11)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0': Cannot resolve reference to bean 'pointcut' while setting bean property 'pointcut'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pointcut': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.aop.aspectj.AspectJExpressionPointcut]: No default constructor found; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1531)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1276)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans(BeanFactoryAdvisorRetrievalHelper.java:92)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findCandidateAdvisors(AbstractAdvisorAutoProxyCreator.java:102)
at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(AspectJAwareAdvisorAutoProxyCreator.java:103)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessBeforeInstantiation(AbstractAutoProxyCreator.java:248)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1037)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1011)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:473)
... 10 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pointcut': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.aop.aspectj.AspectJExpressionPointcut]: No default constructor found; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1155)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1099)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:325)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 26 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.aop.aspectj.AspectJExpressionPointcut]: No default constructor found; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:85)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1147)
... 32 more
Caused by: java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2389)
at java.lang.Class.getConstructor0(Class.java:2699)
at java.lang.Class.getDeclaredConstructor(Class.java:1985)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:80)
... 33 more
Caused by: java.lang.ClassNotFoundException: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 38 more


造成报错的原因是因为缺少两个jar包:aopalliance-1.0.jar和aspectjweaver-1.8.8.jar,

这两个jar包的下载地址:《aop面向切面需要的jar包

将这两个jar包放到项目中,如图:



然后再次运行Test,控制台打印信息如下:



说明spring的aop产生作用了。

接下里我们在Test代码中增加一行调用delete方法的代码,如下:

package com.myspring.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.myspring.service.UserService;

public class Test {

public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml") ;
UserService userService = (UserService) ac.getBean("userService") ;
userService.add() ;
System.out.println("----------------------------");
userService.delete() ;
}

}


运行Test控制台打印信息如下:



我们发现调用的delete方法,并没有加上日志信息。

这是因为我们在beans.xml配置文件中,aop的日志配置,只配置了add方法,

如图:



如果想要UserServiceImpl类中的所有方法都起作用,那么配置应该改成*号,如下面的代码:

<?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"
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"> 
<bean id="userService" class="cn.myspring.service.UserServiceImpl"></bean>
<bean id="log" class="cn.myspring.log.Log"></bean>
<aop:config>
<!-- aop:pointcut切入点配置:
execution() : 是表达式;
*  : 表示所有返回值 ;
位置 : cn.myspring.service.UserServiceImpl ;
方法 : * 表示UserServiceImpl类中的所有方法都起作用 ;
-->
<aop:pointcut expression="execution(* cn.myspring.service.UserServiceImpl.*())" id="pointcut"/>
<!-- aop:advisor : 告诉程序,切入点如嵌入的业务有哪些;
advice-ref  : 对应的公共业务 ;
pointcut-ref: 切入点。
-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
</beans>


再次运行Test代码,控制台打印信息如下:



可以看到在add方法和delete方法的前面都打印了日志信息。

如果我们的方法有参数,那么就可以在beans.xml配置aop时,写上两个点,如下:



如果是某一个包下面的所有的类的所有方法都要使用aop嵌入日志信息,那么就可以写成如下:



后置通知

从spring中文帮助文档.chm中可以看到后置通知的实现类如下图:



在cn.myspring.log包下增加AfterLog类,代码如下:

package cn.myspring.log;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterLog implements AfterReturningAdvice {

/**
* 目标方法执行后,执行的通知
*
* @param returnValue
*            : 返回值 ;
* @param method
*            : 被调用的方法对象 ;
* @param args
*            : 被调用方法的参数 ;
* @param target
*            : 被调用的方法对象的目标对象 。
*/
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName()
+ "被成功执行 , 返回值是 :" + returnValue);
}

}


在beans.xml中配置文件增加afterLog类的配置和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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> 
<bean id="userService" class="cn.myspring.service.UserServiceImpl"></bean>
<bean id="log" class="cn.myspring.log.Log"></bean>
<bean id="afterlog" class="cn.myspring.log.AfterLog"></bean>
<aop:config>
<!-- aop:pointcut切入点配置:
execution() : 是表达式;
*  : 表示所有返回值 ;
位置 : cn.myspring.service.UserServiceImpl ;
方法 : * 表示UserServiceImpl类中的所有方法都起作用 ;
-->
<aop:pointcut expression="execution(* cn.myspring.service.*.*(..))" id="pointcut"/>
<!-- aop:advisor : 告诉程序,切入点如嵌入的业务有哪些;
advice-ref  : 对应的公共业务 ;
pointcut-ref: 切入点。
-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
</beans>


测试类Test代码如下:

package com.myspring.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.myspring.service.UserService;

public class Test {

public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml") ;
UserService userService = (UserService) ac.getBean("userService") ;
userService.add() ;

}

}


运行Test代码,控制台打印信息如下:



AOP的重要性:

spring的aop就是将公共的业务(如:日志、安全等)和领域业务结合。当执行领域业务时,将会把公共业务加进、

来。实现公共业务的重复利用。领域业务更纯粹,程序员专注于领域业务。其本质还是动态代理。

第二种方式实现aop:自定义类来实现

项目结构如图:



Log日志类:

package cn.myspring.log;

/**
* 日志切面
*/
public class Log {

public void before() {
System.out.println("---方法执行前---");
}

public void after() {
System.out.println("---方法执行后---");
}

}


UserService类:

package cn.myspring.service;

/**
* 抽象角色
*/
public interface UserService {

public void add() ;
public void update() ;
public void delete() ;
public void search(int a) ;
}


UserServiceImpl类代码:

package cn.myspring.service;

/**
* 真实角色
*/
public class UserServiceImpl implements UserService {

@Override
public void add() {
System.out.println("增加用户");
}

@Override
public void update() {
System.out.println("修改用户");
}

@Override
public void delete() {
System.out.println("删除用户");
}

@Override
public void search(int a) {
System.out.println("查询用户"+a);
}

}


beans.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"
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">
<bean id="userService" class="cn.myspring.service.UserServiceImpl"></bean>
<bean id="log" class="cn.myspring.log.Log"></bean>
<aop:config>
<!-- 自定义aop配置
ref : 关联Log日志类;
-->
<aop:aspect ref="log">
<aop:pointcut expression="execution(* cn.myspring.service.*.*(..))" id="pointcut"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>

Test类代码:
package com.myspring.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.myspring.service.UserService; public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml") ; UserService userService = (UserService) ac.getBean("userService") ; userService.add() ; } }

运行Test类,控制台打印信息:



第三种实现方式:通过注解来实现

项目结构如下:



Log类代码如下:

package cn.myspring.log;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
* 日志切面
*/
@Aspect
public class Log {

@Before("execution(* cn.myspring.service.*.*(..))")
public void before() {
System.out.println("---方法执行前---");
}

@After("execution(* cn.myspring.service.*.*(..))")
public void after() {
System.out.println("---方法执行后---");
}

}


UserServiceImpl代码:

package cn.myspring.service;

/**
* 抽象角色
*/
public interface UserService {

public void add() ;
public void update() ;
public void delete() ;
public void search(int a) ;
}


UserServiceImpl代码:

package cn.myspring.service;

/**
* 真实角色
*/
public class UserServiceImpl implements UserService {

@Override
public void add() {
System.out.println("增加用户");
}

@Override
public void update() {
System.out.println("修改用户");
}

@Override
public void delete() {
System.out.println("删除用户");
}

@Override
public void search(int a) {
System.out.println("查询用户"+a);
}

}


beans.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"
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">
<bean id="userService" class="cn.myspring.service.UserServiceImpl"></bean>
<bean id="log" class="cn.myspring.log.Log"></bean>
<!-- 自动配置aop -->
<aop:aspectj-autoproxy />
</beans>

Test类同上,运行后控制台打印信息:



当实现环绕通知时,我们在Log类中增加如下代码:

@Around("execution(* cn.myspring.service.*.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法
jp.proceed() ;
System.out.println("环绕后");
}


增加后Log类代码如图:



执行Test类代码,控制台打印信息如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息