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 {

	@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];



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值