动态代理利用Java的反射技术(Java Reflection)生成字节码,在运行时创建一个实现某些给定接口的新类(也称"动态代理类")及其实例。
在基于JDK的动态代理的实现中有两个重要的类:InvocationHandler, Proxy
一个动态代理的示例:
定义一个接口(基于JDK的动态代理只能使用接口)
- public interface ISubject {
- void hello(String param);
- }
为接口定义实现类
- public class SubjectImpl implements ISubject {
-
- @Override
- public void hello(String param) {
- System.out.println("hello " + param);
- }
- }
实现一个代理类:
- public class JDKProxy implements InvocationHandler {
-
- private Object target;
-
- public JDKProxy(Object target) {
- this.target = target;
- }
-
- //创建代理
- public Object newProxy() {
- return (ISubject)Proxy.newProxyInstance(
- target.getClass().getClassLoader(),
- target.getClass().getInterfaces(),
- this);
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-
- System.out.println("---------- 在业务方法调用之前可以进行前置增强 ------------");
-
- //利用反射机制调用方法,invoke为返回值,如果没有返回null
- Object invoke = method.invoke(target, args);
-
- System.out.println("---------- 在业务方法调用之前可以进行后置增强 ------------");
- return invoke;
- }
-
- }
编写代理类实际的调用,利用Proxy类创建代理之后的Subject类
- public class JDKProxyDemo {
-
- public static void main(String[] args) {
-
- ISubject subject = new SubjectImpl();
- JDKProxy subjectProxy = new JDKProxy(subject);
-
- ISubject proxyInstance = (ISubject)subjectProxy.newProxy();
-
- proxyInstance.hello("world");
-
- }
-
- }
运行结果:
- ---------- 在业务方法调用之前可以进行前置增强 ------------
- hello world
- ---------- 在业务方法调用之前可以进行后置增强 ------------
1.什么是AOP?
AOP全称(Aspect Oriented Programming)面向切片编程的简称
AOP的作用:
利用AOP对业务逻辑的各个部分进行隔离,降低业务逻辑的耦合性,提高程序的可重用型和开发效率。
常见的通知
前置通知 方法执行前调用
后置通知 方法执行后调用
返回通知 方法返回后调用
异常通知 方法出现异常调用
环绕通知 动态代理、手动推荐方法运行连接点(Joinpoint)
程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法调用时、异常抛出时、方法返回后等等。切入点(Pointcut)
通知定义了切面要发生的“故事”,连接点定义了“故事”发生的时机,那么切入点就定义了“故事”发生的地点代理(proxy)
应用通知的对象,详细内容参见设计模式里面的动态代理模式。目标(Target)
即被通知的对象适配器(Advisor)
适配器=通知(Advice)+切入点(Pointcut)通知(Advice)
在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
效果如下
demo测试类
- public static void main(String[] args) {
- ApplicationContext cxt= new ClassPathXmlApplicationContext("spring_01.xml");
- IBookService biz =(IBookService) cxt.getBean("bookService");
- List
list = biz.getBooks(); - System.out.println(list);
-
- }
spring的配置文件
- "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 https://www.springframework.org/schema/context/spring-context.xsd">
-
- <bean id="bookDao" class="com.zking.spring.dao.Bookdao">
- bean>
-
- <bean id="bookServiceTarget" class="com.zking.spring.service.BookService">
- <property name="bookdao" ref="bookDao">property>
- bean>
-
-
- <bean id="myMethodBeforeAdvice" class="com.zking.spring.aop.MyMethodBeforeAdvice"/>
-
- <bean id="myAfterReturnAdvice" class="com.zking.spring.aop.MyAfterReturnAdvice"/>
-
- <bean id="myMethodInterceptor" class="com.zking.spring.aop.MyMethodInterceptor"/>
-
- <bean id="myThrowsAdvice" class="com.zking.spring.aop.MyThrowsAdvice"/>
-
- <bean id="bookService" class="org.springframework.aop.framework.ProxyFactoryBean">
-
- <property name="target" ref="bookServiceTarget"/>
-
-
- <property name="interceptorNames">
- <list>
- <value>myMethodBeforeAdvicevalue>
- <value>myAfterReturnAdvicevalue>
- <value>myMethodInterceptorvalue>
- <value>myThrowsAdvicevalue>
- list>
- property>
-
-
- <property name="proxyInterfaces">
- <list>
- <value>com.zking.spring.service.IBookServicevalue>
- list>
- property>
- bean>
-
-
- <bean id="myAdisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
-
-
- <property name="patterns">
- <list>
- <value>.*buyvalue>
- list>
- property>
-
-
- <property name="advice">
- <ref bean="myAfterReturnAdvice"/>
- property>
- bean>
-
-
- <context:component-scan base-package="com.zking.spring"/>
-
-
- <bean id="propPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="locations">
- <list>
- <value>classpath:/project.propertiesvalue>
- list>
- property>
- bean>
-
- beans>
Ibookdao类
- public interface IBookdao {
-
- public List
getBooks(); - }
bookdaoimp类
- @Repository
- public class Bookdao implements IBookdao{
-
- @Override
- public List
getBooks() { - List
b=new ArrayList<>(); - b.add(new Book("sb"));
- b.add(new Book("shabi"));
- b.add(new Book("gobi"));
-
- return b;
- }
- }
注:@Repository是spring容器中dao层的一个组件,此处通过注解方式配置
IbookService类
- public interface IBookService {
-
- public List
getBooks(); -
- // 购书
- public boolean buy(String userName, String bookName, Double price);
-
- // 发表书评
- public void comment(String userName, String comments);
- }
bookService类
- @Service
- public class BookService implements IBookService {
-
- /*@Autowired*/
- private IBookdao bookdao;
-
- public void setBookdao(IBookdao bookdao) {
- this.bookdao = bookdao;
- }
-
- @Override
- public List
getBooks() { - return bookdao.getBooks();
- }
-
- @Override
- public boolean buy(String userName, String bookName, Double price) {
- // 通过控制台的输出方式模拟购书
- System.out.println(userName + " buy " + bookName + ", spend " + price);
- return true;
- }
-
- @Override
- public void comment(String userName, String comments) {
- System.out.println(userName + " say:" + comments);
- }
-
- }
注:Service是spring容器中service层中的一个组件,此处通过注解方式配置
前置通知,需要实现MethodBeforeAdvice,前置通知将在目标对象调用前调用,简单打印。
- public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
-
- @Override
- public void before(Method method, Object[] args, Object target) throws Throwable {
- String b=( "[前置通知]: "
- + this.getClass() + "."
- + method.getName()
- + "将被调用,参数为:"
- + Arrays.toString(args));
-
- System.out.println(b);
- }
-
- }
后置通知,要实现AfterRetruningAdvice
- public class MyAfterReturnAdvice implements AfterReturningAdvice {
- @Override
- public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
- System.out.println("[后置通知]:xxxxxxx");
- }
- }
环绕通知,需要实现MethodInterceptor
- public class MyMethodInterceptor implements MethodInterceptor {
-
- @Override
- public Object invoke(MethodInvocation invocation) throws Throwable {
- //获取目标对象
- Object target = invocation.getThis();
- //获取参数
- Object[] args = invocation.getArguments();
- //获取方法
- Method method = invocation.getMethod();
-
- System.out.println("[环绕通知] 前:将调用"+target.getClass()+"."+method.getName()+"方法,参数为"+ Arrays.toString(args));
-
-
- //调用目标对象上的方法
- Object val = invocation.proceed();
-
- System.out.println("[环绕通知] 前:将调用"+target.getClass()+"."+method.getName()+"方法,参数为"+ val);
-
- return val;
- }
-
- }
异常通知,需要实现ThorwsAdvice
- public class MyThrowsAdvice implements ThrowsAdvice {
-
- //以异常类型作为参数,无返回值
- public void afterThrowing(PriceException e) {
- System.out.println("程序发生了PriceException异常");
- }
- }