1、代理的实现。写一个接口,让目标类和代理类都实现这个接口,并且在代理类中引用目标类,我们在实例化目标类的时候,把目标类的实例当做参数传递给代理类,在代理类里面代替目标类进行操作。
——我们在使用的时候如下,就是实例化代理类的时候,把目标类(被代理类)的实例给代理类的构造器去构造,这背后,其实就是在代理类里面获得被代理对象,然后我们操作代理的时候本质上是代理在自己的类里面调用了被代理类。
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Target target=new Target();
MyProxy p=new MyProxy(target);
p.intro();
p.eat();
}
——首先,我们得写一个接口。
public interface Live {
public void eat();
public void intro();
}
——然后,我们让被代理类实现这个接口。
public class Target implements Live{
public void eat() {
System.out.println("我是目标类,我在吃东西!");
}
public void intro() {
System.out.println("我是目标类!");
}
}
——最后,让代理类也实现这个接口,但是我们写了构造函数,把被代理类的实例拿过来了。
public class MyProxy implements Live {
private Target target;
public MyProxy(Target target){
this.target=target;
}
public void eat() {
target.eat();
}
public void intro() {
target.intro();
}
}
简单代理源代码:JavaEE proxy简单代理示例
2、动态代理。本质上就是不需要上面的那个代理类,而是利用Proxy的newProxyInstance方法动态的创建一个代理对象。代理的目的是什么?主要是拦截,拦截的前后做一些操作和处理。
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final Live target=new Target();
Live p=(Live) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("我是代理,我先自己处理一下,然后再给被代理对象做事!");
Object o=method.invoke(target, args);
System.out.println("我是代理,给被代理对象做事之后,我自己再处理一下!");
return o;
}
});
p.eat(1000.0);
p.intro(1000.0);
}
动态代理源代码:JavaEE动态代理
3、AOP编程思想。(Aspect Oriented Programming)面向切面编程。主要是为了解决两个问题。1)未来的业务方法越来越多,会产生很多重复代码;2)写了很多方法之后,发现需要对这些方法都统一增加一个需求比如日志。
——以我们之前使用的事务为案例。因为我们为了保证事务的执行,需要利用ThreadLocal获得一个统一的Connection。我们在service里面写了获得连接、处理事务的逻辑、关闭连接。我们可以把获得连接、关闭连接2个步骤放在一个代理类里面处理。
——核心逻辑是,我们实现一个代理类,代理上面说过的service,然后在service里面只保留核心的处理事务的逻辑,然后把连接的处理都放在代理类里面去。
4、注解。不是注释。
——注释是写给程序员看的,注解是写给程序看的。
——注解是在JDK1.5后才有的,注解的实际功能用于替换配置文件。
——通过反射来让注解生效。@xxxx。
——JDK中的3个基本注解,实战中很少主动用到。
- @Override,检查子类是否确实覆盖了父类的方法。不写的话也没事,但是写的话,会有代码提示等功能。
- @Deprecated,说明已经过时。
- @SuppressWarning(“unused”)等,是不现实warning信息。比如我们定义了一个变量但是没使用,就会有下划线出现,如果我们在上面添加前面的代码就灭有下划线了;再比如我们我们的List没有加泛型也会有warning,我们使用@SuppressWarning(“rawtypes”)就没有下划线了,所以我们可以看到,这个里面可以增加很多东西,变成如下形式,加上
all
的话就是抑制所有警告信息;
@SuppressWarning({"unused","rawtypes","deprecation"})
5、手写一个注解。注解其实就是一个接口,实现了Annotation接口的接口。它的写法需要注意。
public @interface MyAnnotation {
int age();
String name();
String gender() default "male";
}
使用注解:
public class TestAnnotation {
@MyAnnotation(age=18,name="eric")
public void ff(){
}
}
6、我们实际中使用注解来模仿@Test功能。核心就是,获得对应的类,然后遍历这个类里面的方法,看看哪个方法的上面添加了我们自定义的@MyTest,如果有的话,我们就按照需要的来操作就行。这个操作就是注解的灵魂,也就是具体实现的功能。
——但是,这里有个问题。就是我们先获取类,然后遍历里面方法,判断这个方法是否有我们自定义注解的时候,发现不论有没有,结果都是false。
——原因就在于,我们的注解也需要添加注解(注解的注解叫做元注解)。
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
——我们到元注解Retention里面看看源代码,发现它有一个枚举属性类型,名字就叫做value,如果叫做value的话,使用的时候是可以省略的,直接写值就ok,如上。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
——这个枚举一共3个值,SOURCE、CLASS、RUNTIME,默认是CLASS,这个级别是给虚拟机使用的,但是我们要在程序里面使用的话,最好把这个class文件加载到运行时里面来,所以我们如果要使用这个类的话,我们需要修改这个级别为RUNTIME,这样我们才能取到里面的方法属性等等。
7、还有@Target元注解。是用来指明我们定义的注解是用到哪里的,比如用到类上面(TYPE),还是类里面的方法上面(METHOD),还是属性等等。
8、@Documented。比如我们自定义的一个注解@MyAnno,这个注解上面如果加上@Documented的话,那么用到这个注解的类在生成API文档的时候会出现我们自定义注解的身影,也就是doc文档会出现。
——@ Inherited,表示该注解可以被继承下去。
9、注解的重要知识,Servlet3.0里代替xml配置的注解功能。
——我们目前为止使用的都是JavaEE 5.0,然后对应的Servlet是2.5版本,用的Tomcat7向下兼容的。
——现在我们暂时更改一下配置,使用Servlet3.0,对应的最低配置是Tomcat7和JavaEE 6.0。
——在使用Servlet3.0的时候,我们是可以删除掉web.xml这个配置文件的,因为这个配置文件本质上就是在配置servlet的name、class、url-pattern等等。我们可以用注解来代替它。
——使用方法是,比如我们新建了一个Servlet,我们直接在这个Servlet类的类上面写如下。只写了一个路径,因为其他的name等都有默认值,是可以省略的。这个路径的字段名叫value,所以value也可以省略直接写下面的值即可。
@WebServlet("/servlet/Demo1")
// 或多个配置路径,相当于多个Mapping
@WebServlet({"/servlet/Demo1","/servlet/Demo2"})
——同理,我们之前写Filter也是需要在web.xml中配置的,当然我们也可以用上面这种方法,只不过元注解的关键字换一下:
@WebFilter("/*")
——需要了解利用web.xml和注解的优缺点。
10、类加载器。
——BootStrap。引导类加载器,是类加载器的祖宗,负责加载JRE/lib/rt.jar。JDK中的大部分类就在这里面。
——ExtClassLoader,可扩展类加载器,负责加载JRE/lib/ext/*.jar包。
——AppClassLoader,加载我们放在项目classpath目录里面的所有jar包,也就是我们自己准备的一些jar包,放在WEB-INF的lib里面,以及我们在src里面写的类,生成的class在WEB-INF的classes文件夹里面。
——父类委托机制。说的是,我们加载一个类的时候,是从AppClassLoader->ExtClassLoader->BootStrap后,由BootStrap先找,找不到就到ExtClassLoader找,找不到再去AppClassLoader找,如果还找不到,就返回NotFound异常。保证类只被加载1次。
——不同的类有不同的类加载器来负责加载,我们可以这样测试看看是哪个类加载器加载的。下面的Student是我们自己写的类,所以输出结果是AppClassLoader类加载来加载的。
Student.class.getClassLoader();
——类加载器知识点的目的就是了解我们写的类文件是如何被加载的,路径需要严格控制。举例说明class A extends B
。
——我们先从A开始查找,一路从最下面到最上面的类加载器,然后找来找去,发现AppClassLoader可以找到,找到之后,就在此level向上再循B,因为是继承,所以查找父类只能从找到子类的level开始向上。比如我们在ExtClassLoader找到了,这是正常的。
——而不正常的情况是,如果我们把A没有写在应该写的路径里面,也就是说AppClassLoader找不到,比如我们放在了ExtClassLoader能找到的目录,那么找到A之后,只能向上循B的路径,但是B就在ExtClassLoader,那么就会出错。
——所以,结论就是,我们自定义的类,不要乱放,应该放在正确的classpath下面。