您的位置:首页 > 编程语言 > Java开发

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐