文章目录

  • 1. 类加载器学习
  • 2. 注解学习
  • 2.1. 创建一个自定义注解
  • 2.2. 注解的使用
  • 2.3. 注解的解析
  • 2.4. 元注解
  • 2.5. 注解使用案例一
  • 3. 动态代理学习
  • 3.1. 动态代理案例
  • 3.2. 动态代理总结
  • 4. 用动态代理解决乱码问题


1. 类加载器学习

  1. 类加载器主要是将字节码文件加载成字节码对象。如果要对一个java文件进行修改,可以有两种方式,第一种:在源文件处进行修改。另一种:对加载类对象文件进行修改,通过类加载机制,加载类的反射文件,在运行之前进行动态的修改。

java实现动态word插入值_动态代理解决全局乱码问题

  1. 类加载器种类

java实现动态word插入值_类加载器学习_02

  1. 类加载器都加载哪些东西

java实现动态word插入值_注解学习_03

  • BootStap类加载器是由c语言写的,主要加载jvm运行时一些最基础的一些架包。例如rt.jar,这个就是java运行环境
  • ExtClassLoader主要是加载一些运行时的环境jar包,这些jar包都是扩展的jar包。属于系统的基础包。
  1. 获取类的加载器的方法是
//先获取类的class对象,通过class对象获取类的加载器
Class clazz=Demo.class;
ClassLoader classLoader=clazz.getClassLoader();
  • 类的加载器的主要作用是通过类加载机制方法获取资源的路径:即src目录下的资源。
  • 类加载器是通过某个类的.classLoader()方法,将该类的.class文件从硬盘中加载到java虚拟机中,形成字节码文件。
  • 反射是通过字节码文件对象,将类的字段,方法,构造器等映射成相应的类,并进行各自的操作。

2. 注解学习

  1. 注解和注释有很大不同

注释: 注释是给程序员看的,用来解释类的意思。

注解: 注解是给jvm看的,用于机器编译理解的。代替配置文件。提升开发效率

  1. 常用注解

@Override: 告知编译器,此方法是覆盖父类的方法。

Deprecated: 用于标注过时

SuppressWarnings: 压制警告

2.1. 创建一个自定义注解
  1. 用@interface修饰
public @interface MyAnno{
    //注解的属性必须加括号。
    String name();
}
  1. 注解的属性必须加括弧。
2.2. 注解的使用
  1. 在方法、类、字段上面写@注解名
  2. 注解有多个参数,用逗号隔开。如果注解参数有默认值,可以不用写。
  3. 如果注解属性是value,在使用注解时,(注解传递参数一般是键值对形式),可以不写key,直接写值。

java实现动态word插入值_注解学习_04

java实现动态word插入值_java实现动态word插入值_05

2.3. 注解的解析
  1. 解析目的:获取注解中的参数,原理是通过反射的方式获取。
  2. 有代码:注解创建
public @interface MyAnno {
	//注解的属性
	String name();	
	int age() default 28;
}
  1. 注解测试类
public class MyAnnoTest {
	@SuppressWarnings("all")
	@MyAnno(name = "zhangsan")
	public void show(String str){
		System.out.println("show running...");
	}
}
  1. 注解解析
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
		
		//解析show方法上面的@MyAnno
		//直接的目的是 获得show方法上的@MyAnno中的参数
		//获得show方法的字节码对象
		Class clazz = MyAnnoTest.class;
		Method method = clazz.getMethod("show", String.class);
		//获得show方法上的@MyAnno
		MyAnno annotation = method.getAnnotation(MyAnno.class);
		//获得@MyAnno上的属性值
		System.out.println(annotation.name());//zhangsan
		System.out.println(annotation.age());//28
		//根据业务需求写逻辑代码	
	}
}
  • 注解解析: 通过类反射机制动态获取了测试类的class对象
  • 通过class获取getMethod()的方式获取测试类的方法,然后封装到Method对象中,至于为什么要封装里面,因为Method有一个获取注解的方法,封装是为了获取更多的方法。
  • 获取注解的方法也是通过反射的形式获取的。
2.4. 元注解
  1. 用途: 元注解是注解的注解,用于限定定义主键的特性。比如@Override只能用在方法上,不能修饰类,就是因为有元注解给定义了只能用在方法上。
  2. @Target

java实现动态word插入值_java实现动态word插入值_06

  1. @Retention

java实现动态word插入值_java实现动态word插入值_07

  • 如果通过反射获取注解,必须设置注解的Retention为运行时范围。

java实现动态word插入值_类加载器学习_08

2.5. 注解使用案例一
  1. 本案例测试注解的@Retention和@Target特性
  2. 自定义注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
	//不需要属性
}
  1. TestDemo.java
public class TestDemo {
	//程序员开发中测试用的
	@Test
	public void test1(){
		System.out.println("test1 running...");
	}
	@MyTest
	public void test2(){
		System.out.println("test2 running...");
	}
	@MyTest
	public void test3(){
		System.out.println("test3 running...");
	}	
}
  1. 注解解析类
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
		
		//获得TestDemo
		Class clazz = TestDemo.class;
		//获得所有的方法
		Method[] methods = clazz.getMethods();
		if(methods!=null){
			//获得注解使用了@MyTest的方法
			for(Method method:methods){
				//判断该方法是否使用了@MyTest注解
				boolean annotationPresent = method.isAnnotationPresent(MyTest.class);
				if(annotationPresent){
					//该方法使用MyTest注解了
					method.invoke(clazz.newInstance(), null);
				}
			}
		}
	}
  • 这里面有两个细节问题:一个是:判断获取的方法是否为空,另一个是判断是否是计划中的注解。

3. 动态代理学习

  1. 什么是代理?

代理好比一个中介,比如一个租房中介和房东,房东可以租房,中介也可以租房,这就造成了一个事情,中介和房东拥有相同的方法即:租房,但是中介还可以有其他的方法。

java实现动态word插入值_动态代理_09

  • 由图中可见,代理对象和目标对象拥有一个接口,两个类实现接口中的方法,就形成了两个类拥有相同的方法。
  • 上图属于静态代理,也就是重新建造一个类当做代理对象,而动态代理不需要建立一个新的类,通过反射方法、类的加载机制在内存当中实现代理。
  1. 动态代理
  • proxy类,可以创建一个新的代理对象,可以参考java文档学习。
Object objProxy=Proxy.newProxyInstance(loader,interfaces,h);//根据参数确定是谁的代理对象
  • 根据参数确定是谁的代理对象。
    loader:传递与目标对象相同的类加载器
    interfaces:要求接口的字节码返回数组,所以可以有多个接口
    InvocationHandler接口:要求一个接口的对象,相当于代理类,一般用内部类表示这个参数。该接口是通过匿名内部类的方式写的。
new InvocationHandler() {
    @Override
    //被执行几次?------- 看代理对象调用方法几次
    //代理对象调用接口相应方法 都是调用invoke
   					 /*
					 * proxy:是代理对象
					 * method:代表的是目标方法的字节码对象
					 * args:代表是调用目标方法时参数
					 */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //反射知识点
        Object invoke = method.invoke(target, args);//目标对象的相应方法
        //retrun返回的值给代理对象
        return invoke;
    }
}
  • 这个内部类很重要,特别是这个重写方法,可以在执行方法前对传来的参数进行修改,也可以对执行方法后对获得的返回值进行修改。
3.1. 动态代理案例
  1. 接口
public interface TargetInterface {
	public void method1();
	public String method2();
	public int method3(int x);
}
  1. 目标对象
public class Target implements TargetInterface{

	@Override
	public void method1() {
		System.out.println("method1 running...");
	}

	@Override
	public String method2() {
		System.out.println("method2 running...");
		return "method2";
	}

	@Override
	public int method3(int x) {
		return x;
	}	
}
  1. 代理类
public class ProxyTest2 {
	public static void main(String[] args) {
		final Target target = new Target();
		//动态创建代理对象
		TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
				target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					@Override
					//被执行几次?------- 看代理对象调用方法几次
					//代理对象调用接口相应方法 都是调用invoke
					/*
					 * proxy:是代理对象
					 * method:代表的是目标方法的字节码对象
					 * args:代表是调用目标方法时参数
					 */
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						//反射知识点
						Object invoke = method.invoke(target, args);//目标对象的相应方法
						//retrun返回的值给代理对象
						return invoke;
					}
				}
			);
		proxy.method1();//调用invoke---Method:目标对象的method1方法  args:null  返回值null
		String method2 = proxy.method2();//调用invoke---Method:目标对象的method2方法  args:null  返回值method2
		int method3 = proxy.method3(100);调用invoke-----Method:目标对象的method3方法 args:Object[]{100}  返回值100
		
		System.out.println(method2);
		System.out.println(method3);	
	}	
}
3.2. 动态代理总结
  1. 通过动态代理案例可以看出动态代理的好处是:在invoke()方法中,可以对目标方法的结果进行修改,可以在方法之前做一些事情,在方法之后做一下判断,还可以对方法进行增强,对方法参数修改,拦截。
  2. 动态代理和目标对象必须有一个相同的接口。
  3. 还可以用于方法权限访问,不同角色的人调用的方法有权限,可以通过动态代理的方式进行拦截不同角色的人。

4. 用动态代理解决乱码问题

public class EncodingFilter implements Filter{
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		
		final HttpServletRequest req = (HttpServletRequest) request;
		
		//使用动态代理完成全局编码
		HttpServletRequest enhanceRequset = (HttpServletRequest) Proxy.newProxyInstance(
				req.getClass().getClassLoader(), 
				req.getClass().getInterfaces(), 
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						//对getParameter方法进行增强
						String name = method.getName();//获得目标对象的方法名称
						if("getParameter".equals(name)){
							String invoke = (String) method.invoke(req, args);//乱码
							//转码
							invoke = new String(invoke.getBytes("iso8859-1"),"UTF-8");
							return invoke;
						}
						return method.invoke(req, args);
					}
				}	
			);
		chain.doFilter(enhanceRequset, response);//放行,向servlet层传递。