本文主要是介绍JAVAWEB开发之Servlet3.0新特性的使用以及注解的详细使用和自定义注解的方法、动态代理的使用、利用动态代理实现细粒度的权限控制以及类加载和泛型反射,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
注解
注解介绍:
什么是注解,它有什么作用?
@XXX就是一注解
注释:它是用于描述当前代码功能,是给程序员使用的。
注解:它是描述程序如何运行,是给编译器,解释器,jvm使用的。
注解概述:
从JDK5.0开始,Java增加了对元数据(MetaData)的支持,也就是Annotation(注解)
什么是Annotion,以及注解的作用? 三个基本的Annotion:
- @Override:限定重写父类的方法,该注解只能用于方法。
- @Deprecated:用于表示某个程序元素(类,方法等)已过时。
- @SuppressWarning:抑制编译器警告。
Annotation其实就是代码里的特殊标记,它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术之后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
掌握注解技术的要点:
- 如何定义注解
- 如何反射注解,并根据反射的注解信息,决定如何去运行类。
JDK中自带的三个注解:
(1)@Override:是给编译器使用,用于描述当前的方法是一个重写的方法。
注意:在jdk1.5与jdk1.6之间有区别:
jdk1.5中@Override它只能描述继承中的重写
jdk1.6中@Override它不仅能描述继承中的重写,还可以描述实现中的重写
(2)@Deprecated:它是用于描述方法过时
方法什么时候过时?
1.有新版本的方法替换旧版本的方法
2.在旧的版本中存在安全隐患的方法
(3)@SuppressWarning:去除程序中的警告信息
unused 变量未使用
deprecation 使用了不赞成使用的类或方法时警告
unchecked 执行了未检查的转换时的警告,例如当使用集合时没有用泛型(Generics)
fallthrough 当switch程序块直接通往下一种情况而没有break时的警告
serial 当在可序列化的类上缺少serialVersionUID定义时的警告
finally 任何finally子句不能正常完成时的警告
all 关于以上所有情况的警告
示例:
package cn.itcast.annotation;import java.util.ArrayList;
import java.util.Date;
import java.util.List;@SuppressWarnings("all")
public class AnnotationDemo1 implements A {@Overridepublic String toString() {return super.toString();}//@Overridepublic void show() {}@Deprecatedpublic void print() {}public static void main(String[] args) {Date date = new Date();System.out.println(date.toLocaleString());int i = 10;List list = new ArrayList();}
}interface A {void show();
}
会发现去除了所有警告
注解的应用结构图
关于定义注解:
1.定义注解
@interface 名称 就定义了一个注解
使用方式在类,方法,属性上直接 @名称
问题:@interface 名称,是声明了一个注解,它的本质是什么?
定义一个注解 @interface MyAnnotation{}
用反编译工具DJ Java Decompiler打开
import java.lang.annotation.Annotation;
interface MyAnnotation
extends Annotation
{
}
interface MyAnnotation
extends Annotation
{
}
由此可见注解的本质就是一个接口,它继承了Annotation接口。
所有的注解都实现了这个接口,但是不能手动实现
注解是JDK1.5的新特性
定义注解方式:
定义新的 Annotation 类型使用 @interface 关键字
声明注解的属性
声明注解的属性
- 注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。
- Annotation 的属性声明方式:String name();
- 属性默认值声明方式:String name() default “xxx”;
- 特殊属性value:如果注解中有一个名称value的属性,那么使用注解时可以省略value=部分,如@MyAnnotation(“xxx")
- 特殊属性value[];
2.注解中的成员
接口中的成员:
属性:public static final
方法:public abstract
注解成员:
1.可以有属性
注解中可以有属性,但是基本不用
2.可以有方法
在开发中,一般使用注解时,只研究它的方法,我们一般管它叫做注解中的属性
(1)关于注解中属性的类型问题
它的类型只能是以下几种:
1.基本类型:
整型:byte short int long
浮点:float double
字符:char
逻辑:boolean
2.String
3.Class
4.enum
5.Annotation
6.以上类型的一维数组
(2)关于注解中有属性,使用的问题
如果一个注解中有属性,并且属性没有默认值,那么我们在使用注解时,必须给注解的属性赋值
关于属性赋值的方式:
1.默认值问题String st() default "abc";
2.如果是单值
注解(属性名称=值)
例如:@MyAnnotation3(i=1)
3.如果是数组
1.如果只赋一个值
注解(属性名称=值)
例如:@MyAnnotation3(i=1)
2.如果要赋多个值
注解(属性名称={值1,值2,...})
例如:@MyAnnotation3(i={1,2,3})
4.关于属性名称value问题
可以省略属性名称
例如 @MyAnnotation3("hello");
如果value属性是一个数组:
@MyAnnotation3({"a","b"})
如果注解中有value属性,还有其它属性:
那么value属性名称不能在省略.
(3)元注解
1.@Retention
作用:是指定注解给谁使用.
它的属性值只能是以下三个
RetentionPolicy.SOURCE 给编译器使用 使用后抛弃
作用:是指定注解给谁使用.
它的属性值只能是以下三个
RetentionPolicy.SOURCE 给编译器使用 使用后抛弃
RetentionPolicy.CLASS 给解析器使用。当jvm加载完成后,就抛弃.
具体过程是:编译器将把注解记录在class文件中,当运行Java程序时JVM不会保留该注解 是默认值
RetentionPolicy.RUNTIMEjvm加载完成后,还存在。开发人员可以通过反射来获取注解相关信息
2.@Target
作用:就是定义注解在什么位置使用
3.@Documented
作用:是通过javadoc生成的文档中是否抽取注解描述.
4.@Inherited
作用:是描述当前注解是否具有继承性
想要开发,有功能的注解,对于程序员,一定会使用的元注解是:
@Retention
@Target
RetentionPolicy.RUNTIMEjvm加载完成后,还存在。开发人员可以通过反射来获取注解相关信息
2.@Target
作用:就是定义注解在什么位置使用
3.@Documented
作用:是通过javadoc生成的文档中是否抽取注解描述.
4.@Inherited
作用:是描述当前注解是否具有继承性
想要开发,有功能的注解,对于程序员,一定会使用的元注解是:
@Retention
@Target
示例如下:
package cn.itcast.annotation;// 定义注解
public class AnnotationDemo2 {}@interface MyAnnotation1 {}@interface MyAnnotation {// 基本类型int show();float f();boolean b();// 字符串类型char c();// Class类型Class cl();// 枚举MyEnum m();// 注解类型MyAnnotation1 my1();// 以上类型的一维数组Class[] cls();String[] sts();
}enum MyEnum {A, B, C
}
package cn.itcast.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;@MyAnnotation3
public class AnnotationDemo3 {@MyAnnotation3public void show(){}
}@Target({ElementType.TYPE,ElementType.METHOD})
@interface MyAnnotation3{//String st() default "abc";//int[] i();//String[] value();//int i();String value() default "hello";
}
3.提取Annotation信息
- JDK 5.0 在 java.lang.reflect 包下新增了 AnnotatedElement 接口, 该接口代表程序中可以接受注释的程序元素
- 当一个 Annotation 类型被定义为运行时 Annotation 后, 该注释才是运行时可见, 当 class 文件被载入时保存在 class 文件中的 Annotation 才会被虚拟机读取
- 程序可以调用 AnnotatedElement对象的如下方法来访问 Annotation 信息
Annotation案例一—银行最大转账金额
1.将银行最大转账金额,定义在配置文件中,使用时,直接从配置文件中读取.
2.使用注解来替换配置文件。
1.定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BankInfo {
int maxMoney();
}
2.通过反射来获取注解信息
1.获取当前方法的Method对象。
1.得到Class对象
1.类名.class
2.对象.getClass()
3.Class.forName(String className);
2.得到Method对象
Class.getDeclaredMethod(String methodName,Class...paramClass);
2.在Method类中有一个 getAnnotation(Class annotationClass),可以获取一个注解对象.
3.通过注解对象来调用其属性.
2.使用注解来替换配置文件。
1.定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BankInfo {
int maxMoney();
}
2.通过反射来获取注解信息
1.获取当前方法的Method对象。
1.得到Class对象
1.类名.class
2.对象.getClass()
3.Class.forName(String className);
2.得到Method对象
Class.getDeclaredMethod(String methodName,Class...paramClass);
2.在Method类中有一个 getAnnotation(Class annotationClass),可以获取一个注解对象.
3.通过注解对象来调用其属性.
在src下新建bank.peoperties资源文件
money=5000
新建注解BankInfo
package cn.itcast.annotation.demo1;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BankInfo {int maxMoney();
}
新建转账类Bank
package cn.itcast.annotation.demo1;import java.lang.reflect.Method;//银行最大转账金额5000
@SuppressWarnings("all")
public class Bank {// name1向name2转账money元————使用配置文件完成public void account1(String name1, String name2, int money) {if (money > GlobalField.MONEY) {throw new RuntimeException("最大转账金额为5000");}System.out.println(name1 + "向" + name2 + "转账" + money + "元");}// name1向name2转账money元————使用注解完成@BankInfo(maxMoney = 5000)public void account(String name1, String name2, int money)throws NoSuchMethodException, SecurityException {// 1.获取当前方法的Method对象// 1.1获取当前类的Class对象Class clazz = this.getClass();// 1.2获取当前方法的Method对象Method method = clazz.getDeclaredMethod("account", String.class,String.class, int.class);// 判断当前方法上是否有BankInfo注解boolean flag = method.isAnnotationPresent(BankInfo.class);if (flag) {// 2.在Method类中有一个getAnnotation(Class annotationClass) 可以获取一个注解对象BankInfo bif = method.getAnnotation(BankInfo.class);// 3.通过注解对象来调用其属性int maxMoney = bif.maxMoney();if (money > maxMoney) {throw new RuntimeException("最大转账金额为5000");}System.out.println(name1 + "向" + name2 + "转账" + money + "元");}}
}
新建全局的资源文件读取类GlobalField
package cn.itcast.annotation.demo1;import java.util.ResourceBundle;public class GlobalField {public static final int MONEY = Integer.parseInt(ResourceBundle.getBundle("bank").getString("money"));
}
新建测试类
package cn.itcast.annotation.demo1;public class BankTest {public static void main(String[] args) throws NoSuchMethodException, SecurityException {Bank bank = new Bank();bank.account("张三", "李四", 1000);}
}
注解可以替换配置文件,替换的是什么?
配置文件的出现,它的主要目的就是解耦合。但是随着现在开发程序越来越庞大,配置文件的缺点就出现了,配置文件内容越来越庞大,就不利于我们开发与阅读.这时就出现了注解,因为注解可以直接写在代码上,并且,通过注解也可以解耦合。
Annotation案例二—JDBC连接
创建注解
package cn.itcast.annotation.demo2;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface JdbcInfo {String driverClassName();String url();String username();String password();
}
新建获取连接类
package cn.itcast.annotation.demo2;import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Arrays;public class JdbcUtils {public static Connection getConnection() throws Exception {String driverClassName = "com.mysql.jdbc.Driver";String url = "jdbc:mysql:///mydb1";String username = "root";String password = "root";// 1.加载驱动Class.forName(driverClassName);// 2.获取连接Connection con = DriverManager.getConnection(url, username, password);return con;}@JdbcInfo(driverClassName = "com.mysql.jdbc.Driver", url = "jdbc:mysql:///mydb1", username = "root", password = "root")public static Connection getConnectionByAnnotation(String[] args)throws Exception {System.out.println(Arrays.toString(args));// 得到当前方法上的注解JdbcInfoMethod method = JdbcUtils.class.getDeclaredMethod("getConnectionByAnnotation", String[].class);JdbcInfo jif = method.getAnnotation(JdbcInfo.class);String driverClassName = jif.driverClassName();String url = jif.url();String username = jif.username();String password = jif.password();// 1.加载驱动Class.forName(driverClassName);// 2.获取连接Connection con = DriverManager.getConnection(url, username, password);return con;}public static void main(String[] args) throws Exception {System.out.println(getConnectionByAnnotation(new String[] { "abc" }));}
}
关于映射的使用:简单示例如下:
package reflact;import java.lang.reflect.Method;import org.junit.Test;public class Demo {public static void main(String[] args) {System.out.println(args[0]);}@Testpublic void fun1() throws Exception{Method mainMethod=this.getClass().getDeclaredMethod("main", String[].class);//注意:如果获取的是静态方法,没有对象 所以第一个参数Object为nullmainMethod.invoke(null, (Object)(new String[]{"abc","def"}));}}
Servlet3.0新特性(了解)
在servlet3.0中可以使用注解来替代我们配置文件.
简单说:在servlet3.0中可以没有web.xml文件。
servlet3.0
servlet2.5
问题:怎样知道我们当前使用的是哪个版本?
在web.xml文件中有一个属性version=""它就可以标识当前是哪个版本.
版本对应关系
servlet2.5 javaee5.0 tomcat 5.x tomcat6 jdk1.5
servlet3.0 javaee6.0 tomcat7.0 jdk1.6
简单说:在servlet3.0中可以没有web.xml文件。
servlet3.0
servlet2.5
问题:怎样知道我们当前使用的是哪个版本?
在web.xml文件中有一个属性version=""它就可以标识当前是哪个版本.
版本对应关系
servlet2.5 javaee5.0 tomcat 5.x tomcat6 jdk1.5
servlet3.0 javaee6.0 tomcat7.0 jdk1.6
关于servlet3.0特性:
@WebFilter("/*") 用于配置Filter
@WebListener 用于配置Listener
关于这些注解细节:
以@WebServlet("/hello") 为例
注意:属性urlpatterns与values它们都是描述访问当前servlet的路径,但它们不能一起出现,只能使用一个.
<servlet>
<servlet-name></servlet-name> String name() default "";
<servllet-class></servlet-class>
<init-param> WebInitParam[] initParams() default {};
<param-name>
<param-value>
</init-param>
<load-on-startup> int loadOnStartup() default -1;
</servlet>
<servlet-mapping>
<servlet-name></servlet-name>
<url-pattern></url-pattern> String[] urlPatterns() default {}; String[] value() default {};
</servlet-mapping>
在servlet中怎样获取初始化参数
ServletConfig对象获取
在web.xml文件中的属性 metadata-complete,可以取值为true,false,
如果为false,代表servlet3.0中的注解可以使用,如果为true,代表不可以使用注解。
1.method=post
2.encType="multipart/form-data"
3.使用<input type="file" name="f">
服务器端:
servlet3.0完成。
1.要在servlet上添加注解@MultipartConfig
表示Servlet接收multipart/form-data 请求
2.在servlet中要想得到上传信息,通过request对象获取一个Part对象。
Part part=request.getPart();
part.write(String filename);
问题:
1.关于上传文件中文名称乱码问题
因为上传是post请求,直接使用post乱码解决方案就可以 request.setCharacterEncoding("utf-8");
2.关于获取上传文件名称
通过Part获取一个header
String cd = part.getHeader("Content-Disposition");
在这个header中包含了上传文件名称,直接截取出来就可以。
String filename = cd.substring(cd.lastIndexOf("\\") + 1,cd.length() - 1);
3.如果多文件上传怎样处理?
request.getParts();
1.必须在注解添加一项
@WebServlet(value = "/reg", asyncSupported = true)
asyncSupported=true,代表当前servlet支持异步操作.
2.需要一个异步 上下文对象,通过这个对象,可以获取request,response对象.
AsyncContext context = req.startAsync();
还可以对异步上下文进行监听,在它的监听器方法中有一个onComplete,可以用于判断结束。
1.使用注解来替换配置文件
@WebServlet("/hello") 用于配置servlet@WebFilter("/*") 用于配置Filter
@WebListener 用于配置Listener
关于这些注解细节:
以@WebServlet("/hello") 为例
注意:属性urlpatterns与values它们都是描述访问当前servlet的路径,但它们不能一起出现,只能使用一个.
<servlet>
<servlet-name></servlet-name> String name() default "";
<servllet-class></servlet-class>
<init-param> WebInitParam[] initParams() default {};
<param-name>
<param-value>
</init-param>
<load-on-startup> int loadOnStartup() default -1;
</servlet>
<servlet-mapping>
<servlet-name></servlet-name>
<url-pattern></url-pattern> String[] urlPatterns() default {}; String[] value() default {};
</servlet-mapping>
在servlet中怎样获取初始化参数
ServletConfig对象获取
在web.xml文件中的属性 metadata-complete,可以取值为true,false,
如果为false,代表servlet3.0中的注解可以使用,如果为true,代表不可以使用注解。
2.servlet3.0中的文件上传
浏览器端:1.method=post
2.encType="multipart/form-data"
3.使用<input type="file" name="f">
服务器端:
servlet3.0完成。
1.要在servlet上添加注解@MultipartConfig
表示Servlet接收multipart/form-data 请求
2.在servlet中要想得到上传信息,通过request对象获取一个Part对象。
Part part=request.getPart();
part.write(String filename);
问题:
1.关于上传文件中文名称乱码问题
因为上传是post请求,直接使用post乱码解决方案就可以 request.setCharacterEncoding("utf-8");
2.关于获取上传文件名称
通过Part获取一个header
String cd = part.getHeader("Content-Disposition");
在这个header中包含了上传文件名称,直接截取出来就可以。
String filename = cd.substring(cd.lastIndexOf("\\") + 1,cd.length() - 1);
3.如果多文件上传怎样处理?
request.getParts();
3.servlet3.0中异步处理
本质就是在服务器端开启一个线程,来完成其它的操作。1.必须在注解添加一项
@WebServlet(value = "/reg", asyncSupported = true)
asyncSupported=true,代表当前servlet支持异步操作.
2.需要一个异步 上下文对象,通过这个对象,可以获取request,response对象.
AsyncContext context = req.startAsync();
还可以对异步上下文进行监听,在它的监听器方法中有一个onComplete,可以用于判断结束。
在Servlet3.0中 创建自己的filter拦截器和监听器以及Servlet如下:
拦截器
package cn.itcast.web.filter;import java.io.IOException;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.annotation.WebFilter;//@WebFilter("/*")
public class MyFilter implements Filter {@Overridepublic void destroy() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {System.out.println("my filter");chain.doFilter(request, response);}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}}
监听器
package cn.itcast.web.listener;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;//@WebListener
public class MyListener implements ServletContextListener {@Overridepublic void contextDestroyed(ServletContextEvent arg0) {}@Overridepublic void contextInitialized(ServletContextEvent arg0) {System.out.println("myListener");}}
Servlet
package cn.itcast.web.servlet;import java.io.IOException;import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;//@WebServlet(urlPatterns = { "/hello", "/h1" },initParams={@WebInitParam(name="username",value="tom"),@WebInitParam(name="encode",value="utf-8")})
public class MyServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {doPost(req, resp);}@Overridepublic void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {System.out.println("hello servlet");// 获取初始化参数ServletConfig config = this.getServletConfig();String username = config.getInitParameter("username");System.out.println(username);}
}
上传文件示例:
UploadServlet
package cn.itcast.web.servlet;import java.io.IOException;import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {doPost(req, resp);}@Overridepublic void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {req.setCharacterEncoding("utf-8");// 解决乱码Part part = req.getPart("f"); // 得到上传文件信息.// req.getParts();// 获取上传文件名称String cd = part.getHeader("Content-Disposition");System.out.println(cd); // form-data; name="f";// filename="C:\Users\Administrator\Desktop\鎹曡幏.PNG"String filename = cd.substring(cd.lastIndexOf("\\") + 1,cd.length() - 1);System.out.println(filename);part.write("d:/upload/"+filename);// 完成文件上传.}}
客户端jsp上传代码
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head><title>My JSP 'index.jsp' starting page</title>
</head><body><form action="${pageContext.request.contextPath}/upload" method="post"enctype="multipart/form-data"><input type="file" name="f"><input type="submit" value="上传"></form>
</body>
</html>
动态代理
1.代理模式
代理模式的作用:
屏蔽真实行为的访问,让程序更加安全。
可以对真实行为的调用进行控制
代理模式实现:
(1)代理类与被代理类要实现同一接口
(2)在代理类中持有被代理对象
(3)在代理类中调用被代理的行为
AOP:面向切面编程(AOP的底层实现就是通过动态代理做到的)
示例代码如下:
package cn.itcast.proxy;public class ProxyTest {public static void main(String[] args) {(new PersonProxy(new Person())).say("hello");}
}interface DoSomething {public void say(String word);
}class Person implements DoSomething {@Overridepublic void say(String word) {System.out.println(word);}}class PersonProxy implements DoSomething {private DoSomething dos;public PersonProxy(DoSomething dos) {this.dos = dos;}@Overridepublic void say(String word) {dos.say(word);}}
2.动态代理
- 在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。
- 并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求,例如:解决web工程乱码、拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。
3.动态代理实现
有两种方式:
方式1.通过jdk中提供的Proxy类来实现
这种方式要求,被代理类必须实现接口。
简单说,只能为接口做代理.
方式2.通过cglib来实现。
它不要求,实现接口。
代码实现:
Proxy类中有一个方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);
参数:
loader:
要求,传递的是被代理类的类加载器ClassLoader.
类加载器怎样获取:
得到其Class对象。在Class类中提供一个方法 getClassLoader();
interfaces:
要求:得到被代理对象所实现的接口的所有Class对象。
怎样获取所有实现接口的Class对象?
得到其Class对象,在Class类中提供一个方法 getInterfaces();
它返回的是Class[],就代表所实现接口的所有Class对象。
h:
它的类型是InvocationHandler,这是一个接口。
InvocationHandler 是代理实例的调用处理程序 实现的接口。
InvocationHandler接口中有一个方法invoke;
// 参数 proxy就是代理对象
// 参数method就是调用方法
// 参数args就是调用的方法的参数
// 返回值,就是真实行为执行后返回的结果,会传递给代理对象调用的方法.
public Object invoke(Object proxy, Method method, Object[] args);
有两种方式:
方式1.通过jdk中提供的Proxy类来实现
这种方式要求,被代理类必须实现接口。
简单说,只能为接口做代理.
方式2.通过cglib来实现。
它不要求,实现接口。
代码实现:
Proxy类中有一个方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);
参数:
loader:
要求,传递的是被代理类的类加载器ClassLoader.
类加载器怎样获取:
得到其Class对象。在Class类中提供一个方法 getClassLoader();
interfaces:
要求:得到被代理对象所实现的接口的所有Class对象。
怎样获取所有实现接口的Class对象?
得到其Class对象,在Class类中提供一个方法 getInterfaces();
它返回的是Class[],就代表所实现接口的所有Class对象。
h:
它的类型是InvocationHandler,这是一个接口。
InvocationHandler 是代理实例的调用处理程序 实现的接口。
InvocationHandler接口中有一个方法invoke;
// 参数 proxy就是代理对象
// 参数method就是调用方法
// 参数args就是调用的方法的参数
// 返回值,就是真实行为执行后返回的结果,会传递给代理对象调用的方法.
public Object invoke(Object proxy, Method method, Object[] args);
动态代理案例1—实现编码过滤
新建一个Filter过滤编码
package cn.itcast.proxy.demo;import java.io.IOException;
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.HttpServletResponse;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对象的代理对象reqProxyHttpServletRequest 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() {}}
配置
<filter><filter-name>encodingFilter</filter-name><filter-class>cn.itcast.proxy.demo.EncodingFilter</filter-class></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
............
动态代理案例二—动态代理+注解实现的细粒度的权限控制
因为URL级别的权限控制是属于粗粒度的,一旦一个Servlet处理多个请求 就无法使用
SQL脚本
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.得到当前登录的用户
我们在所有的service的方法上添加了一个User参数。
那么我们获取时,就可以直接通过invoke方法的args参数获取.
User user = (User) args[0];
1.首先判断用户是否存在,也就是判断它是否登录了。
2.如果登录了,根据用户查询数据库,得到这个用户所具有的所有权限名称
SELECT
privileges.name
FROM
users,PRIVILEGES,userprivilege
WHERE
users.id=userprivilege.user_id
AND
privileges.id=userprivilege.privilege_id
AND
users.id=?";
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.得到当前登录的用户
我们在所有的service的方法上添加了一个User参数。
那么我们获取时,就可以直接通过invoke方法的args参数获取.
User user = (User) args[0];
1.首先判断用户是否存在,也就是判断它是否登录了。
2.如果登录了,根据用户查询数据库,得到这个用户所具有的所有权限名称
SELECT
privileges.name
FROM
users,PRIVILEGES,userprivilege
WHERE
users.id=userprivilege.user_id
AND
privileges.id=userprivilege.privilege_id
AND
users.id=?";
项目 具体代码如下:
封装数据源获取类DataSourceUtils
package cn.itcast.utils;import java.sql.Connection;
import java.sql.SQLException;import javax.sql.DataSource;import com.mchange.v2.c3p0.ComboPooledDataSource;public class DataSourceUtils {private static ComboPooledDataSource cpds = new ComboPooledDataSource();public static Connection getConnection() throws SQLException {return cpds.getConnection();}public static DataSource getDataSource() {return cpds;}}
package cn.itcast.book.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface BookInfo {String value();// 即权限名称
}
UserDao
package cn.itcast.book.dao;import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;import cn.itcast.book.domain.User;
import cn.itcast.utils.DataSourceUtils;public class UserDao {// 登录操作public User findUserByUserNameAndPassword(String username, String password)throws SQLException {String sql = "select * from users where username=? and password=?";QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());return runner.query(sql, new BeanHandler<User>(User.class), username,password);}
}
User
package cn.itcast.book.domain;public class User {private int id;private String username;private String password;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}
BookService
package cn.itcast.book.service;import cn.itcast.book.annotation.BookInfo;
import cn.itcast.book.domain.User;public interface BookService {@BookInfo("添加图书")public void addBook(User user) throws Exception;@BookInfo("修改图书")public void updateBook(User user) throws Exception;@BookInfo("删除图书")public void deleteBook(User user) throws Exception;public void searchBook(User user) throws Exception;
}
BookServiceImpl
package cn.itcast.book.service;import cn.itcast.book.domain.User;public class BookServiceImpl implements BookService {public void addBook(User user) throws Exception {System.out.println("book add.");}public void updateBook(User user) throws Exception {System.out.println("book update");}public void deleteBook(User user) throws Exception {System.out.println("book delete");}public void searchBook(User user) throws Exception {System.out.println("book search");}}
UserService
package cn.itcast.book.service;import java.sql.SQLException;import cn.itcast.book.dao.UserDao;
import cn.itcast.book.domain.User;public class UserService {public User login(String username, String password) throws SQLException {return new UserDao().findUserByUserNameAndPassword(username, password);}
}
BookServiceFactory
package cn.itcast.book;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ColumnListHandler;import cn.itcast.book.annotation.BookInfo;
import cn.itcast.book.domain.User;
import cn.itcast.book.service.BookService;
import cn.itcast.book.service.BookServiceImpl;
import cn.itcast.utils.DataSourceUtils;public class BookServiceFactory {private static BookService service = new BookServiceImpl();public static BookService getInstance() {BookService proxy = (BookService) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// 真实行为访问前--判断用户是否有权限执行当前行为boolean flag = method.isAnnotationPresent(BookInfo.class);if (!flag) {// 不需要权限return method.invoke(service, args);}// 1.得到Method方法要想访问需要的权限BookInfo bif = method.getAnnotation(BookInfo.class);String pname = bif.value();System.out.println("需要的权限是: " + pname);// 2.得到当前用户User user = (User) args[0];if (user == null) {throw new RuntimeException("没有登录,请登录后操作");}// 3.从数据库中查询出当前用户所具有的的所有权限名称String sql = "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());System.out.println("当前用户是" + user.getUsername() + ",它具有的权限是:"+ pnames);if (pnames.contains(pname)) {Object obj = method.invoke(service, args);return obj;} else {throw new RuntimeException("权限不足");}}});return proxy;}
}
BookServlet
package cn.itcast.book.servlet;import java.io.IOException;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import cn.itcast.book.BookServiceFactory;
import cn.itcast.book.domain.User;
import cn.itcast.book.service.BookService;public class BookServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String method = request.getParameter("method");if ("add".equals(method)) {add(request, response);} else if ("update".equals(method)) {update(request, response);} else if ("delete".equals(method)) {delete(request, response);} else if ("search".equals(method)) {search(request, response);}}public void add(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {BookService service = BookServiceFactory.getInstance();User user = (User) request.getSession().getAttribute("user");try {service.addBook(user);} catch (Exception e) {e.printStackTrace();}}public void update(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {BookService service = BookServiceFactory.getInstance();User user = (User) request.getSession().getAttribute("user");try {service.updateBook(user);} catch (Exception e) {e.printStackTrace();}}public void delete(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {BookService service = BookServiceFactory.getInstance();User user = (User) request.getSession().getAttribute("user");try {service.deleteBook(user);} catch (Exception e) {e.printStackTrace();}}public void search(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {BookService service = BookServiceFactory.getInstance();User user = (User) request.getSession().getAttribute("user");try {service.searchBook(user);} catch (Exception e) {e.printStackTrace();}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}
LoginServlet
package cn.itcast.book.servlet;import java.io.IOException;
import java.sql.SQLException;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import cn.itcast.book.domain.User;
import cn.itcast.book.service.UserService;public class LoginServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//1.得到请求参数String username=request.getParameter("username");String password=request.getParameter("password");//2.调用service完成登录操作UserService service=new UserService();try {User user=service.login(username, password);if(user!=null){request.getSession().setAttribute("user", user);response.sendRedirect(request.getContextPath()+"/book.jsp");return;}} catch (SQLException e) {e.printStackTrace();}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}
book.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'login.jsp' starting page</title>
</head><body>当前用户:${user.username}<br><hr><a href="${pageContext.request.contextPath}/book?method=add">bookadd</a><br><a href="${pageContext.request.contextPath}/book?method=update">bookupdate</a><br><a href="${pageContext.request.contextPath}/book?method=delete">bookdelete</a><br><a href="${pageContext.request.contextPath}/book?method=search">booksearch</a><br>
</body>
</html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'login.jsp' starting page</title>
</head><body><form action="${pageContext.request.contextPath}/login" method="get">username:<input type="text" name="username"><br>password:<input type="password" name="password"><br> <inputtype="submit" value="登录"></form>
</body>
</html>
类加载器
- 类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象
- 当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:
1.类加载器
问题:什么是类加载器,有什么作用?
类加载器的作用就是将java中的字节码文件(.class文件)转换成Class对象。
当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:
1.引导类加载器 BootStrap jre/lib/rt.jar
2.扩展类加载器 ExtClassLoader JRE/lib/ext/*.jar
3.应用类加载器(系统类加载器) AppClassLoader SystemClassLoader CLASSPATH指定的所有jar或目录
在java中ClassLoader代表类加载器,所有的类加载器都是ClassLoader的子.
问题:什么是类加载器,有什么作用?
类加载器的作用就是将java中的字节码文件(.class文件)转换成Class对象。
当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:
1.引导类加载器 BootStrap jre/lib/rt.jar
2.扩展类加载器 ExtClassLoader JRE/lib/ext/*.jar
3.应用类加载器(系统类加载器) AppClassLoader SystemClassLoader CLASSPATH指定的所有jar或目录
在java中ClassLoader代表类加载器,所有的类加载器都是ClassLoader的子.
演示类加载器:
问题:类加载器如果获取?
在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才能生效,并且类只加载一次的原因。
泛型反射
在BaseDaoImpl类中需要得到当前这个类上的泛型的Class对象,而直接通过T.class这是不对的.public class BaseDaoImpl<T> implements BaseDao<T> {
public T findById(int id) {
// Session session=HibernateUtils.getSession();
// session.get(T.class,id);
return null;
}
怎样得到当前这个类上的泛型的Class?
Type type = this.getClass().getGenericSuperclass(); // 得到当前类上的泛型--父类型
Type[] params = ((ParameterizedType) type).getActualTypeArguments(); // 得到当前类上所有的泛型类型Class
clazz = (Class) params[0];
这篇关于JAVAWEB开发之Servlet3.0新特性的使用以及注解的详细使用和自定义注解的方法、动态代理的使用、利用动态代理实现细粒度的权限控制以及类加载和泛型反射的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!