美文网首页
Spring学习day-63:代理模式和AOP

Spring学习day-63:代理模式和AOP

作者: 开源oo柒 | 来源:发表于2019-10-20 20:52 被阅读0次

    一、Spring代理模式

    1.什么是代理模式?

    为其他对象提供一种代理以控制对这个对象的访问;
    代理模式的三要素:
    (1)抽象的类或者接口 ---完成一件怎样的事情;
    (2)被代理对象---事情操作具体内容 ;
    (3)代理对象----帮助我们完成事情的同是可以增加其他的功能 。

    • 具体例子:找中介租房子

    (1)抽象的类或者接口--租房子
    (2)被代理对象---房东
    (3)代理对象---中介

    • 代理模式的好处:

    (1)房东可以安心的做自己的事情--(被代理对象可以做自己的事情);
    (2)我们有了问题直接找中介---(被代理对象变得比较的安全);
    (3)可以增强代码的扩展性 ;实现对目标对象原有的功能增强,即扩展目标对象的功能方法。

    • 代理模式
      (1)静态代理
      (2)动态代理(JDK动态代理,CGLIB动态代理)

    2.静态代理:

    • 缺点:

    随着被代理对象的增多,我们发现代理对象的压力越来越大,而且里面书写的代码也是比较臃肿的。

    • 代码实现:
      (1)出租房子的接口
    package com.zlw.proxy1;
    public interface LetRoom {
        //出租房子的方法
        public void zf();
    
    }
    

    (2)房东1

    package com.zlw.proxy1;
    public class FD implements LetRoom {
        //房东1
        @Override
        public void zf() {
            System.out.println("FD出租房子!位置....");
        }
    }
    

    (3)房东2

    package com.zlw.proxy1;
    
    public class FD2 implements LetRoom {
         //房东2
        @Override
        public void zf() {
            System.out.println("FD2出租房子!地点....");
        }
    }
    

    (4)中介

    package com.zlw.proxy1;
    
    public class ZJ implements LetRoom {
        private int money;//中介收取的费用
        
        public ZJ(int money) {
            this.money = money;
        }
        
        //中介
        @Override
        public void zf() {
            System.out.println("收取中介费100");
            
            if(money<=800){
                FD fd = new FD();
                fd.zf();
            }else if(money>800){
                FD2 fd2 = new FD2();
                fd2.zf();
            }
            System.out.println("收取管理费500");
        }
    }
    

    (5)租客

    package com.zlw.proxy1;
    
    public class My {
        public static void main(String[] args) {
            //ZJ zj = new ZJ(1200);//大于800
            ZJ zj = new ZJ(500);//小于800
            zj.zf();
        }
    }
    
    • 结果


      大于800
      小于800

    3.JDK动态代理:

    • 什么是动态代理:

    在不修改原有类的基础上,为原来类添加新的功能;不需要依赖某个具体业务。

    • JDK动态代理:

    JDK是基于反射机制,生成一个实现代理接口的匿名类,然后重写方法,实现方法的增强;目标类是接口。

    • 缺点:

    我们发现JDK代理模式是必须有接口的操作,如果没有对应的接口,这个时候JDK代理就没有办法使用;它是只能针对接口编程的。

    • 代码实现:
      (1)接口:
    package com.zlw.proxy2;
    
    public interface LetRoom {
        public void zf();
    }
    

    (2)房东1:

    package com.zlw.proxy2;
    
    public class FD implements LetRoom{
    
        @Override
        public void zf() {
            System.out.println("FD出租房子1....");
        }
    }
    

    (3)房东2:

    
    public class FD2 implements LetRoom{
    
        @Override
        public void zf() {
            System.out.println("FD2出租房子....");
        }
    }
    

    (4)代理对象:

    package com.zlw.proxy2;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    //在这个类中书写动态产生代理对象的方法
    public class MyInv implements InvocationHandler {
        private LetRoom lr;
    
        public void setLr(LetRoom lr) {
            this.lr = lr;
        }
        
        //执行该方法就会动态产生代理对象
        public Object getProxy(){
            Object o = Proxy.newProxyInstance(MyInv.class.getClassLoader(), new Class[]{LetRoom.class}, this);
            
            return o;
        }
        //这个方法类似于书写中介中的zf()方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            /**
             * proxy:代理对象
             * 
             * method:代理对象中的方法
             * 
             * args:参数-null
             */
            System.out.println("推荐费100");
            //调用了指定方法的zf()
            Object invoke = method.invoke(lr, args);
            System.out.println("收取管理费500");
            return invoke;
        }
    }
    

    (5)测试:

    package com.zlw.proxy2;
    
    public class Test {
        
        public static void main(String[] args) {
            MyInv my = new MyInv();
            
            //设置具体的房东
            my.setLr(new FD());
            
            //产生了中介代理对象
            LetRoom proxy = (LetRoom) my.getProxy();
            proxy.zf();
    //      Util.writeProxyClassToHardDisk("E:/$Proxy11.class",new FD().getClass().getInterfaces() );
        }
    }
    
    结果

    4.CGLIB动态代理:

    • 什么是CGLIB动态代理:

    CGLIB是基于继承机制,继承被代理类,所以方法不能声明为final,然后重写父类方法达到增强了类的作用;非接口类,非final类。
    它底层是基于asm第三方框架,是对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    • 如何实现CGLIB代理代理?

    (1)导包:
    cglib-2.2.2.jar;
    asm-3.3.1.jar;
    javassist-3.17.1-GA.jar;
    (2)实现MethodInterceptor接口;重写intercept方法;
    (3)产生代理类对象;

    • JDK动态代理和CGLIB动态代理区别:

    JDK动态代理的被代理者必须实现任意接口;
    CGLib动态代理不用实现接口,是通过继承实现的;

    • 代码实现:
      (1)房东
    package com.zlw.proxy3;
    
    public class FD {
        public void zf(){
            System.out.println("房东出租房子,地址....");
        }
    }
    

    (2)中介:CGLIB代理

    package com.zlw.proxy3;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class MethodInv implements MethodInterceptor{
        //产生代理类对象
        public Object getProxy(){
            Enhancer eh = new Enhancer();
            //类似于之前的接口
            eh.setSuperclass(FD.class);
            eh.setCallback(this);
            
            //是整个设置内容生效
            Object o = eh.create();
            return o;   
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
             /**
             * o:被代理对象
             *
             * method:被代理对象的方法
             *
             * objects:参数
             *
             * methodProxy:代理对象中方法
    
             * */
            System.out.println(method+"---"+objects+"---"+methodProxy);
            
            System.out.println("收取管理费100");
            
            //调用真正的房东
            Object o1 = methodProxy.invokeSuper(o, objects);
            return o1;
        }
    }
    

    (3)测试:

    
    public class Test {
        public static void main(String[] args) {
            MethodInv mi = new MethodInv();
            FD proxy = (FD) mi.getProxy();
            proxy.zf();
        }
    }
    
    结果

    二、Spring+MyBatis实现简单登录

    1.代码实现:

    (1)导包:

    asm-3.3.1.jar
    cglib-2.2.2.jar
    commons-logging-1.1.1.jar
    javassist-3.17.1-GA.jar
    log4j-1.2.17.jar
    log4j-api-2.0-rc1.jar
    log4j-core-2.0-rc1.jar
    mybatis-3.2.7.jar
    mybatis-spring-1.2.3.jar
    mysql-connector-java-5.1.47.jar
    slf4j-api-1.7.5.jar
    slf4j-log4j12-1.7.5.jar
    spring-aop-4.1.6.RELEASE.jar
    spring-beans-4.1.6.RELEASE.jar
    spring-context-4.1.6.RELEASE.jar
    spring-context-support-4.1.6.RELEASE.jar
    spring-core-4.1.6.RELEASE.jar
    spring-expression-4.1.6.RELEASE.jar
    spring-jdbc-4.1.6.RELEASE.jar
    spring-tx-4.1.6.RELEASE.jar

    (2)applicationContext配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <!-- (1)连接数据库获得数据源 -->
        <bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql://localhost:3306/user"></property>
            <property name="username" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
       
        <!-- 获取sqlsession工厂对象 -->
        <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="ds"></property>
            <property name="typeAliasesPackage" value="com.zlw.pojo"></property>
        </bean>
        
        <!--扫描mapper文件  -->
        <bean id="mapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="factory"></property>
            <property name="basePackage" value="com.zlw.mapper"></property>
        </bean>
        
        <!-- 配置service的实现类 -->
        <bean id="admins" class="com.zlw.service.impl.AdminServiceImpl">
            <property name="adminMapper" ref="adminMapper"></property>
        </bean>
    </beans>
    

    (3)mapper接口和配置文件:

    package com.zlw.mapper;
    
    import com.zlw.pojo.Admin;
    
    public interface AdminMapper {
        public Admin selectOne(String uname,String pwd);
    
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.zlw.mapper.AdminMapper">
        <select id="selectOne" resultType="com.zlw.pojo.Admin">
            select * from t_admin where uname = #{0} and pwd=#{1}
        </select>
      </mapper>
    

    (4)Servlet

    package com.zlw.controller;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.zlw.pojo.Admin;
    import com.zlw.service.AdminService;
    
    @WebServlet("/adminSerlvet")
    public class AdminServlet extends HttpServlet {
        AdminService admins;
    
        @Override
        public void init() throws ServletException {
            // 读取全局参数app对应的参数值:applicationContext
    
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            admins = app.getBean("admins", AdminService.class);
        }
    
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 获取页面信息
            String uname = request.getParameter("uname");
            String pwd = request.getParameter("pwd");
            // 处理信息
            Admin admin = admins.login(uname, pwd);
            // 跳转页面
            if (admin != null) {
                response.sendRedirect(request.getContextPath() + "/index.jsp");
            } else {
                request.setAttribute("error", "登录失败!");
                request.getRequestDispatcher("/login.jsp").forward(request, response);
            }
        }
    }
    

    (5)jsp登录界面

    <body>
        <h4>用户登录</h4>
        <form action="adminSerlvet" method="post">
            <p>
                用户名:<input type="text" name="uname" /><span style="color: red">${error }</span>
            </p>
            <p>
                密码:<input type="password" name="pwd" />
            </p>
            <p>
                <input type="submit" value="登录" />
            </p>
        </form>
    </body>
    

    三、SpringAOP的简介

    1.什么是AOP?

    AOP(Aspect Oriented Programming):面向切面编程通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术;利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    2.AOP中的要素:

    切点:在执行的每一个方法都可以看作是一个切点 ;
    通知:就是我们需要扩展的功能代码 ;
    在切点之前增加的通知称之为前置通知 ;
    在切点之后增加的通知称之为后置通知 ;
    在切点之前和切点之后都增加的通知称之为环绕通知 ;
    在切点代码中有异常会执行的通知称之为异常通知 ;
    切面:有了切点+通知就可以织成切面 ;

    3.AOP实现的方式:

    (1)Schema base :把前置通知和后置通知都写到一个通知中,组成了环绕通知。
    (2)AspectJ :通过AspectJ方式获取在通知中获取切点的参数。


    示意图

    相关文章

      网友评论

          本文标题:Spring学习day-63:代理模式和AOP

          本文链接:https://www.haomeiwen.com/subject/cbpamctx.html