类加载器就加载字节码文件(.class)
类加载器有三种,不同加载器加载不同
怎么获得类加载器(重点)
package demo;
import java.net.URL;
public class Demo {
public static void main(String[]args) {
//获得Demo字节码文件的类加载器
Class clazz = Demo.class;//获得Demo的字节码对象
ClassLoader classLoader = clazz.getClassLoader();//获得Demo的类加载器
//getResource的参数路径相对classes(src)
URL jdbc_properties_url = classLoader.getResource("demo/jdbc.properties");//获得classes(src)下的任何资源地址
String path = jdbc_properties_url.getPath();
System.out.println(path);
}
}
//打印结果/Users/wangzhe/Documents/workspace/WEB25/build/classes/demo/jdbc.properties
注解就是符合一定格式的语法 @xxxx
注解作用:
注解在目前而言最主流的应用:代替配置文件
如在创建web项目时选项Dynamic web module version3.0以上没有web.xml文件,是因为被注解取代,如下:
package demo;
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;
@WebServlet("/demo")
public class DemoServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
此时@WebServlet就代替在web.xml中配置url-pattern等相关配置。
关于配置文件与注解开发的优缺点:
@Override:告知编译器此方法是覆盖父类的(可以在方法上用),帮助检查覆盖父类的方法是否正确
@Deprecated:标注过时
@SuppressWarnings:压制警告(可以在方法上用,也可以在类上用,还可以在属性上用)
发现的问题:
不同的注解只能在不同的位置使用(方法上、字段上、类上)
实例代码:
package annotation;
import java.util.ArrayList;
import java.util.List;
public class AnnoDemo {
public static void main(String[] args) {
@SuppressWarnings(value = { "rawtypes", "unused" })//rawtypes压制泛型的警告、unused压制未使用的警告,all压制所有警告
List list = new ArrayList();
show();
}
@Deprecated
public static void show() {}
public static void show(String xx) {}
@Override
public String toString() {
return "AnnoDemo []";
}
}
思路
编写一个注解
定义注解关键字:@interface
注解的属性
语法:返回值 名称();
注意:如果属性的名字是value,并且注解的属性值有一个那么在使用注解时可以省略value
如定义名为value的字符串类型属性,使用时如下:
//定义如下
public @interface MyAnno {
String value();
}
//使用如下
public class MyAnnoTest {
@MyAnno("xxx")
public void show(){}
}
//定义如下
public @interface MyAnno {
String[] value();
}
//使用如下
public class MyAnnoTest {
@MyAnno({"xxx","bbb"})
public void show(){}
}
//定义如下
public @interface MyAnno {
//注解的属性
String name();
int age() default 28;
}
//使用如下
public class MyAnnoTest {
@MyAnno(name="aaa",age=10)
public void show(){}
}
注解属性类型只是以下几种
基本类型
使用注解
在类、方法、字段上面使用@XXX
解析使用了注解的类
注意:
isAnnotationParsent annotationClass>
:判断该字节码对象身上是否使用该注解了getAnnotation(Class annotationClass)
:获得该字节码对象身上的注解对象实例代码:
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
//注解的属性
String name();
int age() default 28;
}
package annotation;
public class MyAnnoTest {
@MyAnno(name="aaa",age=10)
public void show(String str){
System.out.println("show running......"+str);
}
}
package annotation;
import java.lang.reflect.Method;
public class MyAnnoParser {
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());
System.out.println(annotation.age());
}
}
编写注解MyTest用于修饰方法,且注解在整个阶段都可见。此注解的作用仅做修饰,用于被注解修饰的方法需要执行某些内容。
package case1;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
//不需要属性
}
编写使用该注解的类。
package case1;
import org.junit.Test;
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...");
}
}
编写执行对应解析内容的类,先通过字节码对象找到对应的类,获取所有的方法,再通过方法筛选被@MyTest注解修饰的方法,最后执行对应内容(本demo执行的是对应方法体)
package case1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyTestParster {
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);
}
}
}
}
}
输出结果
test3 running...
test2 running...
动态代理:不用手动编写一个代理对象,不需要一一编写与目标对象相同的方法,这个过程,在运行时 的内存中动态生成代理对象。(字节码对象级别的代理对象 )
动态代理的API
在jdk的API中存在一个Proxy中存在一个生成动态代理的的方法newProxyInstance
返回值类型 | 方法 |
---|---|
static Object | newProxyInstance(ClassLoader loader,Class[]interfaces,InvoationHandlerh) |
* 返回值:Object就是代理对象
* 参数1:loader代表与目标对象相同的类加载器(目标对象.getClass().getClassLoader())
* 参数2:interfaces代表与目标对象实现的所有的接口字节码对象数组
* 参数3:h具体的代理的操作,InvacationHandler接口
注意:JDK的Proxy方式实现的动态代理,目标对象必须有接口,没有接口不能实现jdk版动态代理
代理对象与接口的关系
实例代码(利用动态代理的内容为目标对象进行动态代理)
package proxy;
public interface TargetInterface {
public void method1();
public String method2();
public int method3(int x);
}
package proxy;
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;
}
}
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
public class ProxyTest {
@Test
public void test1() {
//获得动态的代理对象(在运行时,内容中动态的为Target创建一个虚拟的代理对象)
//objProxy是代理对象 根据参数确定到底是谁的代理对象
/*
* 参数一ClassLoader loader:与目标对象相同的类加载器
* 参数二Class>[] interfaces:接口的字节码对象数组
* 参数三InvocationHandler h:具体的代理的操作,InvocationHandler接口
*/
TargetInterface objProxy = (TargetInterface)Proxy.newProxyInstance(Target.class.getClassLoader(), new Class[]{TargetInterface.class}, new InvocationHandler() {
//invoke()代表的是执行代理对象的方法
@Override
/*
* 参数二Method method:代表目标对象的方法字节码对象
* 参数三Object[] args:代表目标对象相应方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("目标方法前的逻辑");
//执行目标对象的方法
Object invoke = method.invoke(new Target(), args);
System.out.println("目标方法后的逻辑");
return invoke;
}
});
objProxy.method1();
String method2 = objProxy.method2();
System.out.println(method2);
}
}
执行结果:
目标方法前的逻辑
method1 running...
目标方法后的逻辑
目标方法前的逻辑
method2 running...
目标方法后的逻辑
method2
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
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方法被执行几次?看代理对象调用方法几次
//代理对象调用接口相应的方法,都是调用invoke()
/*
* 参数一Object proxy:指的就是代理对象
* 参数二Method method:指的是目标方法的字节码对象
* 参数三Object[] args:指的是调用目标方法时的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行相关处理操作...
//利用反射的知识点
Object invoke = method.invoke(target, args);//目标对象的相应方法
//执行相关处理操作...
return invoke;//return返回的值给代理对象
}
});
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);
}
}
执行结果:
method1 running...
method2 running...
method2
100
案例DEMO:使用动态代理完成全局编码
package filter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class EncodingFilter2 implements Filter{
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)arg0;
//使用动态代理完成全局编码
HttpServletRequest enhanceRequest = (HttpServletRequest)Proxy.newProxyInstance(request.getClass().getClassLoader(), request.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(request,args);
//转码
invoke = new String(invoke.getBytes("ISO8859-1"),"UTF-8");
return invoke;
}
return method.invoke(request, args);
}
});
arg2.doFilter(enhanceRequest, arg1);
}
@Override
public void init(FilterConfig arg0) throws ServletException {}
}
总结