动静代理
- 代理对象存在的价值:次要用于拦挡对实在业务对象的拜访。
- 代理对象有什么办法?
- 当初要生成某一个对象的代理对象,这个代理对象通常也要编写一个 类来生成,所以首先要编写用于生成代理对象的类。
- 如何编写生成代理对象的类,两个因素:
- 代理谁
- 如何生成代理对象
- 代理谁?
- 设计一个类变量,以及一个构造函数,记住代理类 代理哪个对象。
- 如何生成代理对象?
- 设计一个办法生成代理对象(在办法内编写代码生成代理对象是此处编程的难点)
- 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
: 要求,传递的是 被代理类 的类加载器 ClassLoaderinterfaces
: 要求: 失去 被代理对象 所实现的 接口 的所有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 解决多个申请?
book add
book update
book delete
book search
- 在 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 对象.
泛型反射
==== 动静代理
- 代理对象存在的价值:次要用于拦挡对实在业务对象的拜访。
- 代理对象有什么办法?
- 当初要生成某一个对象的代理对象,这个代理对象通常也要编写一个 类来生成,所以首先要编写用于生成代理对象的类。
- 如何编写生成代理对象的类,两个因素:
- 代理谁
- 如何生成代理对象
- 代理谁?
- 设计一个类变量,以及一个构造函数,记住代理类 代理哪个对象。
- 如何生成代理对象?
- 设计一个办法生成代理对象(在办法内编写代码生成代理对象是此处编程的难点)
- 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
: 要求,传递的是 被代理类 的类加载器 ClassLoaderinterfaces
: 要求: 失去 被代理对象 所实现的 接口 的所有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 解决多个申请?
book add
book update
book delete
book search
- 在 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 类动静代理
====
- 代理对象存在的价值:次要用于拦挡对实在业务对象的拜访。
- 代理对象有什么办法?
- 当初要生成某一个对象的代理对象,这个代理对象通常也要编写一个 类来生成,所以首先要编写用于生成代理对象的类。
- 如何编写生成代理对象的类,两个因素:
- 代理谁
- 如何生成代理对象
- 代理谁?
- 设计一个类变量,以及一个构造函数,记住代理类 代理哪个对象。
- 如何生成代理对象?
- 设计一个办法生成代理对象(在办法内编写代码生成代理对象是此处编程的难点)
- 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
: 要求,传递的是 被代理类 的类加载器 ClassLoaderinterfaces
: 要求: 失去 被代理对象 所实现的 接口 的所有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 解决多个申请?
book add
book update
book delete
book search
- 在 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]; 中须要失去以后这个类上的泛型的 Class 对象,而间接通过 T.class 这是不对的.
- 怎么失去以后这个类上的泛型的 Class?
- Type type = this.getClass().getGenericSuperclass(); // 失去以后类上的泛型–父类型
- Type[] params = ((ParameterizedType) type).getActualTypeArguments(); // 失去以后类上所有的泛型类型 Class
- clazz = (Class) params[0];
- 问题: 在 BaseDaoImpl 类中须要失去以后这个类上的泛型的 Class 对象,而间接通过 T.class 这是不对的.
- 怎么失去以后这个类上的泛型的 Class?
- Type type = this.getClass().getGenericSuperclass(); // 失去以后类上的泛型–父类型
- Type[] params = ((ParameterizedType) type).getActualTypeArguments(); // 失去以后类上所有的泛型类型 Class
- clazz = (Class) params[0];