注解
注解介绍:
什么是注解,它有什么作用?
@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.RUNTIME jvm加载完成后,还存在。开发人员可以通过反射来获取注解相关信息 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的路径,但它们不能一起出现,只能使用一个. String name() default ""; WebInitParam[] initParams() default {}; int loadOnStartup() default -1; String[] urlPatterns() default {}; String[] value() default {}; 在servlet中怎样获取初始化参数 ServletConfig对象获取 在web.xml文件中的属性 metadata-complete,可以取值为true,false, 如果为false,代表servlet3.0中的注解可以使用,如果为true,代表不可以使用注解。
2.servlet3.0中的文件上传
浏览器端: 1.method=post 2.encType="multipart/form-data" 3.使用 服务器端: 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"%>
My JSP 'index.jsp' starting page
动态代理
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() {
}
}
配置
encodingFilter
cn.itcast.proxy.demo.EncodingFilter
encodingFilter
/*
新建一个提交表单输入中文进行测试
............
动态代理案例二—动态代理+注解实现的细粒度的权限控制
因为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处理多个请求? 可以通过在请求,携带参数来判断要做什么操作.book add book update book delete book search 在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.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 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"%>
My JSP 'login.jsp' starting page
类加载器
类加载器负责将 .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 implements BaseDao { 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];