我在上海乐字节学习的第二十四天(持续更新中)

84 篇文章 0 订阅
66 篇文章 0 订阅

动态代理

代理对象存在的价值:主要用于拦截对真实业务对象的访问。

代理对象有什么方法?

现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。

如何编写生成代理对象的类,两个要素:

代理谁

如何生成代理对象

代理谁?

设计一个类变量,以及一个构造函数,记住代理类 代理哪个对象。

如何生成代理对象?

设计一个方法生成代理对象(在方法内编写代码生成代理对象是此处编程的难点)

Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:

1.生成代理对象使用哪个类装载器

2.生成哪个对象的代理对象,通过接口指定

3.生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定。

初学者必须必须记住的2件事情:

Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。

由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。

代理模式

代理模式作用:

屏蔽真实行为的访问,让程序更加安全。

可以对真实行为的调用进行控制。

通过一个案例:来说明代理的实现以及代理的作用

代理模式实现:

1.代理类与被代理类要实现同一个接口.

//潘金莲 ---被代理

public class Pjl implements KindWoman{



    public void throwEye(){

        System.out.println("潘金莲抛媚眼");



    }



    public void doSomething(){

        System.out.println("潘金莲。。。。。。。。");

    }

}







//王婆 ---代理

public class Wp implements KindWoman {



    private KindWoman woman;



    public Wp(KindWoman woman) {

        this.woman = woman;

    }



    public void throwEye() {

        woman.throwEye();



    }



    public void doSomething() {

        woman.doSomething();

    }



}

2.在代理类中持有被代理对象.

public class Xmq {



    public static void main(String[] args) {



        KindWoman woman = new Pjl();



        Wp wp = new Wp(woman);



        wp.throwEye();//真实执行的是潘金莲,但是我们看不到,所以屏蔽了真实行为。

    }

}

3.在代理类中调用被代理的行为。

 public void throwEye() {

        //在这里做操作,可以控制是否调用真实行为。



        woman.throwEye();



        //在这个位置,可以在真实行为调用完成后,在做操作。

    }



    public void doSomething() {

        woman.doSomething();

    }

}

AOP:面向切面的编程

AOP的底层实现就是通过动态代理来做到的。

动态代理

在代理模式基础上发展的,它不在是对单一的类型进行代理,而是可以对任意的一个实现了接口的类的对象做代理。

动态代理实现

两种方式:

这种方式要求,被代理类必须实现接口,即只能为接口做代理.

1.通过jdk中提供的Proxy类来实现

2.通过cglib来实现,它不要求实现接口。

第一种方式的代码实现:

loader: 要求,传递的是被代理类的类加载器ClassLoader

interfaces: 要求:得到被代理对象所实现的接口的所有Class对象。

h: 它的类型是InvocationHandler,这是一个接口。

得到其Class对象,在Class类中提供一个方法getClassLoader();

类加载器怎样获取:

得到其Class对象,在Class类中提供一个方法 getInterfaces();它返回的是Class[],就代表所实现接口的所有Class对象。

怎样获取所有实现接口的Class对象?

InvocationHandler 是代理实例的调用处理程序 实现的接口。

Proxy类中有一个方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);

参数:

InvocationHandler接口中有一个方法invoke;

public Object invoke(Object proxy, Method method, Object[] args);

参数 proxy 就是代理对象

参数 method 就是调用方法

参数 args 就是调用的方法的参数

返回值,就是真实行为执行后返回的结果,会传递给代理对象调用的方法.

eg:

public class Student implements Person {



    public String say(String message) {

        return "hello " + message;

    }



}





public class StudentProxyTest {



    public static void main(String[] args) {

        // 做Person接口实现类Student的动态代理。



        // 1.创建一个Student 被代理

        final Person s = new Student();



        // 2.得到s的代理对象.

        Person sproxy = (Person) Proxy.newProxyInstance(s.getClass()

                .getClassLoader(), s.getClass().getInterfaces(),

                new InvocationHandler() {

                    // 参数 proxy就是代理对象

                    // 参数method就是调用方法

                    // 参数args就是调用的方法的参数

                    // 返回值,就是真实行为执行后返回的结果,会传递给代理对象调用的方法.

                    public Object invoke(Object proxy, Method method,

                            Object[] args) throws Throwable {



                        // proxy,就是代理对象,我们一般不使用。

                        // method,就是要访问的方法。

                        // args 就是要访问的方法的参数

                        return method.invoke(s, args); // s.say("james");



                    }

                });

        String message = sproxy.say("james"); // 这个是代理对象调用say方法.



        System.out.println(message);

    }

}

动态代理案例1—实现编码过滤

public class LoginServlet extends HttpServlet {



    public void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        String username = request.getParameter("username");

        System.out.println(username);



    }



    public void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        doGet(request, response);

    }



}

2.操作

public class EncodingFilter implements Filter {



    public void init(FilterConfig filterConfig) throws ServletException {



    }



    public void doFilter(ServletRequest request, ServletResponse response,

            FilterChain chain) throws IOException, ServletException {



        // 1.强转

        final HttpServletRequest req = (HttpServletRequest) request;

        HttpServletResponse resp = (HttpServletResponse) response;



        // 2.操作

        // 创建一个req对象的代理对象reqProxy

        HttpServletRequest reqProxy = (HttpServletRequest) Proxy

                .newProxyInstance(req.getClass().getClassLoader(), req

                        .getClass().getInterfaces(), new InvocationHandler() {



                    public Object invoke(Object proxy, Method method,

                            Object[] args) throws Throwable {



                        // 1.得到方法名称

                        String methodName = method.getName();

                        if ("getParameter".equals(methodName)) {

                            String param = req.getParameter((String) (args[0]));



                            return new String(param.getBytes("iso8859-1"),

                                    "utf-8");



                        } else {

                            // 不是getParameter方法,就执行其原来操作.

                            return method.invoke(req, args);

                        }

                    }

                });

        // 3.放行

        chain.doFilter(reqProxy, resp);

    }



    public void destroy() {



    }



}

动态代理案例2–细粒度的权限控制
数据库

create table users(

 id int primary key auto_increment,

 username varchar(40),

 password varchar(40)

);





insert into users values(null,'aaa','111');

insert into users values(null,'bbb','111');

insert into users values(null,'ccc','111');









create table privileges(

  id int primary key auto_increment,

  name varchar(40)

);





insert into privileges values(null,'添加图书');

insert into privileges values(null,'修改图书');

insert into privileges values(null,'查看图书');

insert into privileges values(null,'删除图书');

多对多表关系

create table userprivilege(

  user_id int ,

  privilege_id int,

  foreign key(user_id) references users(id),

  foreign key(privilege_id) references privileges(id),

  primary key(user_id,privilege_id)

);





insert into userprivilege values(1,1);

代码实现:

1.完成登录操作,将user存储到session中.

login.jsp LoginServlet UserService UserDao.

2.登录成功,跳转到book.jsp页面。

可以通过在请求,携带参数来判断要做什么操作

在这个页面上有四个超连接,访问的是同一个servlet(BookServlet)

问题:怎样让一个servlet处理多个请求?

<a href="${pageContext.request.contextPath}/book?method=add">book add</a>

<br>

<a href="${pageContext.request.contextPath}/book?method=update">book update</a>

<br>

<a href="${pageContext.request.contextPath}/book?method=delete">book delete</a>

<br>

<a href="${pageContext.request.contextPath}/book?method=search">book search</a>

在servlet中判断method值是什么,调用不同的请求处理方法.

这种方式下,在做权限控制时,如果使用url级别权限控制,就不能通过判断请求的资源路径来处理。

可以使用细粒度权限控制:

实现原理:使用注解+动态代理来完成。

注解:它用于定义当前行为的访问需要什么权限。

动态代理帮助我们完成控制拦截。简单说,就是在代理中,会判断当前用户是否具有访问该行为的权限,如果有会调用被代理的行为,如果没有,不调用行为,直接抛出权限不足。

3.实现权限控制

1.创建一个BookInfo注解,它是用于描述行为访问时,需要什么权限的.

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Inherited

public @interface BookInfo {



String value(); //这就是权限名称

}

2.在BookServiceFactory中进行权限控制

1.得到当前行为访问需要的权限名称

BookInfo bif = method.getAnnotation(BookInfo.class);

String pname = bif.value();

2.得到当前登录的用户

User user = (User) args[0];

我们在所有的service的方法上添加了一个User参数。

那么我们获取时,就可以直接通过invoke方法的args参数获取.

1.首先判断用户是否存在,也就是判断它是否登录了。

  User user = (User) args[0];



    if (user == null) {

        throw new RuntimeException("没有登录,请登录后操作");

    }
* 2.如果登录了,根据用户查询数据库,得到这个用户所具有的所有权限名称

    * 使用ColumnListHandler进行封装查询结果。

SELECT 

    privileges.name 

FROM 

    users,PRIVILEGES,userprivilege 

WHERE 

    users.id=userprivilege.user_id 

AND 

    privileges.id=userprivilege.privilege_id 

AND 

    users.id=?";
QueryRunner runner = new QueryRunner(DataSourceUtils

        .getDataSource());



List<Object> pnames = runner.query(sql,

        new ColumnListHandler(), user.getId());

判断用户是否具有权限

           // 真实行为访问前--判断用户是否有权限执行当前行为

                boolean flag = method.isAnnotationPresent(BookInfo.class);



                if (!flag) {

                    // 不需要权限

                    return method.invoke(service, args);



                }







if (pnames.contains(pname)) {



                    Object obj = method.invoke(service, args);



                    // 真实行为访问 后

                    return obj;

                } else {

                    throw new RuntimeException("权限不足");

类加载器

什么是类加载器,有什么作用?

1.引导类加载器 BootStrap jre/lib/rt.jar

2.扩展类加载器 ExtClassLoader JRE/lib/ext/*.jar

3.应用类加载器(系统类加载器) AppClassLoader SystemClassLoader CLASSPATH指定的所有jar或目录

类加载器的作用就是将java中的字节码文件(.class文件)转换成Class对象。

当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:

在java中ClassLoader代表类加载器,所有的类加载器都是ClassLoader的子.

bootstrap classloader:引导(也称为原始)类加载器,它负责加载Java的核心类。这个加载器的是非常特殊的,它实际上不是 java.lang.ClassLoader的子类,而是由JVM自身实现的。可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库:

URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();

for (int  i = 0; i < urls.length; i++) {

  System.out.println(urls[i].toExternalForm());

}

因为JVM在启动的时候就自动加载它们,所以不需要在系统属性CLASSPATH中指定这些类库

演示类加载器

在Class类中有一个方法 getClassLoader()它返回的就是一个类加载器.

问题:类加载器如果获取?

1.获取引导类加载器

ClassLoader cl = String.class.getClassLoader();

System.out.println(cl);

结果是null.

原因:引导类加载器特殊,它根本就不是java实现。所有在得到引导类回载器是结果就是null.

2.扩展类加载器

ClassLoader cl = AccessBridge.class.getClassLoader();

System.out.println(cl); //sun.misc.Launcher$ExtClassLoader@9cb0f4

3.应用类加载器

ClassLoader cl = this.getClass().getClassLoader();

System.out.println(cl); //sun.misc.Launcher$AppClassLoader@164dbd5

全盘负责委托机制

全盘负责:即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的其它Class通常也由这个classloader负责载入。

委托机制:先让parent(父)类加载器 寻找,只有在parent找不到的时候才从自己的类路径中去寻找。

类加载还采用了cache机制:如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么修改了Class但是必须重新启动JVM才能生效,并且类只加载一次的原因。

自定义类加载器

创建了一个类 javax.activation.MimeType,在这个类中有一个方法show();当jvm加载这个类时,因为在rt.jar包下也存在一个MimeType类,并且包名都一样,这时jvm就会使用引导类加载器加载这个类,而我们想得到的其实是应该由应用类加载器加载的Class.

解决方案:自定义类加载器.

1.创建一个类,去继承自ClassLoader

2.重写findClass方法,在这个方法中通过defineClass将一个.class文件转换成Class对象.

泛型反射

问题:在BaseDaoImpl类中需要得到当前这个类上的泛型的Class对象,而直接通过T.class这是不对的.

怎样得到当前这个类上的泛型的Class?

Type type = this.getClass().getGenericSuperclass(); // 得到当前类上的泛型–父类型

Type[] params = ((ParameterizedType) type).getActualTypeArguments(); // 得到当前类上所有的泛型类型Class

clazz = (Class) params[0];

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果需要在这个字节增加其他内容,可以将多个字节按照一定的顺序存储到字节。具体方法如下: ``` #define DOIP_VERSION 2 // 声明一个长度为4的字节串 unsigned char byte_array[4]; // 将DOIP_VERSION转换成一个字节,并存储到字节的第一个元素 byte_array[0] = DOIP_VERSION & 0xFF; // 假设需要在字节增加一个无符号短整型数(2个字节),可以按照大端字节序(高位在前,低位在后)存储到字节 unsigned short value = 0x1234; byte_array[1] = (value >> 8) & 0xFF; // 存储高位字节 byte_array[2] = value & 0xFF; // 存储低位字节 // 假设需要在字节增加一个无符号整型数(4个字节),可以按照大端字节序存储到字节 unsigned int number = 0x12345678; byte_array[3] = (number >> 24) & 0xFF; // 存储高位字节 byte_array[4] = (number >> 16) & 0xFF; byte_array[5] = (number >> 8) & 0xFF; byte_array[6] = number & 0xFF; // 存储低位字节 ``` 在上面的代码,我们先声明了一个长度为4的字节串byte_array,然后将DOIP_VERSION转换成一个字节,并存储到字节的第一个元素byte_array[0]。 接着,我们假设需要在字节增加一个无符号短整型数(2个字节)和一个无符号整型数(4个字节),分别将它们按照大端字节序存储到字节。 为了按照大端字节序存储数据,我们需要先将数据按照高位在前、低位在后的顺序存储到一个无符号整型变量,然后再将其按照字节序存储到字节。具体方法是,使用位运算符和按位与运算符,将数据的每个字节从高到低截取出来,然后存储到字节。 需要注意的是,在存储数据时,需要考虑数据在字节的存储顺序,这里使用的是大端字节序,即高位字节在前,低位字节在后。如果使用的是小端字节序,即低位字节在前,高位字节在后,那么存储数据的顺序需要进行相应的调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值