注解
注解介绍:
什么是注解,它有什么作用?
@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资源文件
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];