JAVAWEB开发之Servlet3.0新特性的使用以及注解的详细使用和自定义注解的方法、动态代理的使用、利用动态代理实现细粒度的权限控制以及类加载和泛型反射
2017-03-03 23:14
1926 查看
注解
注解介绍:什么是注解,它有什么作用?
@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 { @Override public String toString() { return super.toString(); } //@Override public void show() { } @Deprecated public 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
{
}
由此可见注解的本质就是一个接口,它继承了Annotation接口。
所有的注解都实现了这个接口,但是不能手动实现
注解是JDK1.5的新特性
定义注解方式:
定义新的 Annotation 类型使用 @interface 关键字
声明注解的属性
注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。
Annotation 的属性声明方式:String name();
属性默认值声明方式:String name() default “xxx”;
特殊属性value:如果注解中有一个名称value的属性,那么使用注解时可以省略value=部分,如@MyAnnotation(“xxx")
特殊属性value[];
注解支持类型:String、基本数据类型、枚举、Class 、其它注解类型、以上数据类型相应一维数组
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.CLASS 给解析器使用。当jvm加载完成后,就抛弃.
具体过程是:编译器将把注解记录在class文件中,当运行Java程序时JVM不会保留该注解
是默认值
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 { @MyAnnotation3 public 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.通过注解对象来调用其属性.
在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)); // 得到当前方法上的注解JdbcInfo Method 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]); } @Test public void fun1() throws Exception{ Method mainMethod=this.getClass().getDeclaredMethod("main", String[].class); //注意:如果获取的是静态方法,没有对象 所以第一个参数Object为null mainMethod.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特性:
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 { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("my filter"); chain.doFilter(request, response); } @Override public 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 { @Override public void contextDestroyed(ServletContextEvent arg0) { } @Override public 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 { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override public 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 { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override public 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 { @Override public void say(String word) { System.out.println(word); } } class PersonProxy implements DoSomething { private DoSomething dos; public PersonProxy(DoSomething dos) { this.dos = dos; } @Override public 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—实现编码过滤
新建一个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对象的代理对象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() { } }
配置
<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=?";
项目 具体代码如下:
封装数据源获取类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; } }
新建权限注解BookInfo
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() { @Override public 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">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> <br> </body> </html>
login.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> <form action="${pageContext.request.contextPath}/login" method="get"> username:<input type="text" name="username"><br> password:<input type="password" name="password"><br> <input type="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的子.
演示类加载器:
问题:类加载器如果获取?
在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开发之mybatis详解(一)——mybatis的入门(实现增删改查操作)、自定义别名、抽取代码块以及动态SQL的使用
- 【Servlet3.0新特性】第02节_Servlet的动态注册以及使用注解编写监听器和过滤器
- 使用DBUtils、动态代理以及注解实现事务控制
- 通过JDK动态代理和自定义注解来控制方法级别的权限访问
- JavaWeb开发知识总结(annotation,Servlet3.0,文件上传,动态代理)
- JAVAWEB开发之事务详解(mysql与JDBC下使用方法、事务的特性、锁机制)和连接池的详细使用(dbcp以c3p0)
- Java使用注解和动态代理实现方法调用时的日志记录示例
- Android开发之利用注解简单实现动态权限申请
- JAVAWEB开发之Struts2详解(一)——Struts2框架介绍与快速入门、流程分析与工具配置以及Struts2的配置以及Action和Result的详细使用
- 使用动态代理+自定义注解控制数据库事务
- 利用动态代理+注解 实现子线程中执行方法
- springAOP与自定义注解实现细粒度权限控制管理
- 通过使用反射+动态代理+注解来实现对事务的控制
- [Java]利用拦截器和自定义注解做登录以及权限验证
- JavaWeb开发之使用jQuery与Ajax实现动态联级菜单效果
- JAVA动态代理和方法拦截(使用CGLib实现AOP、方法拦截、委托)
- 在struts2.3.4.1中使用注解、反射、拦截器实现基于方法的权限控制
- 【JavaWeb-14】代理Proxy、动态代理、AOP编程思想、注解、元注解、类加载器
- Java web自定义标签按钮级别权限控制完美诠释(jplogic 快速开发平台)
- JAVAWEB开发之Servlet Filter(过滤器)详解包括post和get编码过滤器、URL访问权限控制、自动登录。以及装饰模式的使用