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中可以看到后置通知的实现类如下图:
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类代码,控制台打印信息如下: