JavaWeb-21
泛型,注解,Servlet3.0,Log4j
今天任务:
1、泛型概述
2、Dao模式下泛型的引入
3、注解及反射注解
4、Servlet3.0
5、Log4j(明白怎么用,就够)
一、了解泛型
1、了解泛型:
GenericTestDemo1.java
package com.itheima.generic;
/**
* 泛型类中的所有实例方法都可以不用声明,不包含静态方法
* @author wangli
*
* @param <T>
*/
public class GenericTestDemo1<T> {
//泛型在使用时,必须 先声明,再去使用 ,如何声明? 在返回值前面加一个声明 <T>
/*public <T> void m1(T t){
t=null;
}
public <T> T m2(T t){
return t;
}*/
public void m1(T t){
t=null;
}
public T m2(T t){
return t;
}
public static <T> void m3(T t){
}
public <K,V> V m4(K k){ //------------> Map<String,Object>
V v = null;
return v;
}
}
实验:项目:day2100generic:com.itheima.generic
项目架构:
GenerivTestDemo1.java
GenericTestDemo1
{
//泛型在使用时,必须先声明,再去使用,如何声明?在返回值前面加一个声明
public <T> void m1(T t){
t = null;
}
public <T> T m2(T t){
return t;
}
//在每个方法上都加<T>,麻烦,可以直接定义一个泛型<T>的类
}
public <T> void GenericTestDemo1
{
public void m1(T t){
t = null;
}
public T m2(T t){
return t;
}
//public static void m3(T t){}类里不能加静态方法
public <K,V> V ma4(K k){
V v = null;
return v;
}
}
GenericDemo2.java
{
public static <T> void swap(T []t,int index1,int index2){
T temp = t[index1];
t[index1] = t[index2];
t[index2] = temp;
}
public static <T> void reverse(T []t){
int start = 0;
int end = t.length-1;
//交换
for (int i = 0; i<t.length ;i++ ){
if(start<end){
//交换
swap(t,start,end);
start++;
end--;
}
}
}
}
Demo2Test.java
{
main(){
Integer [] a = {12,34,56,78,90,54};//使用泛型类:不要用基本类型int
//GenericTestDemo.swap(a,1,2);
GenericTestDemo2.reverse(a);
System.out.println("Array.asList(a)");
}
}
GenericTestDemo2.java
package com.itheima.generic;
public class GenericTestDemo2 {
public static <T> void swap(T []t ,int index1,int index2){
T temp = t[index1];
t[index1]=t[index2];
t[index2]=temp;
}
public static <T> void reverse(T []t){
int start =0;
int end = t.length-1;
for (int i = 0; i < t.length; i++) {
if(start<end){
//交换
swap(t,start,end);
start++;
end--;
}
}
}
}
Demo2Test.java
package com.itheima.generic;
import java.util.Arrays;
public class Demo2Test {
/**
* @param args
*/
public static void main(String[] args) {
Integer []a = {12,34,56,78,89,54};//不要基本类型
//GenericTestDemo2.swap(a, 1, 2);
GenericTestDemo2.reverse(a);
System.out.println(Arrays.asList(a));
}
}
结果:
二、Dao模式下泛型的引入(重点)
2、Dao模式下泛型的引入(重点)
BookDao
{
public void add(Book book);
public void update(Book book);
//public void delete(String id);
//public void delete(int id);
//不断给delete重载,没完没了
public void delete(Serializable id);//以后可以放入String , int
//查看integer的源码发现高手是这样处理的
public Book findOne(Serializable id);//因为Serializable是所有基本类型的接口
public List findAll();
}
Book,User--->序列化!
Userdao
{
public void add(User book);
public void update(User book);
//public void delete(String id);
//public void delete(int id);
//不断给delete重载,没完没了
public void delete(Serializable id);//以后可以放入String , int
//查看integer的源码发现高手是这样处理的
public User findOne(Serializable id);
public List findAll();
}
发现以上很多公共的代码
把Book/Userdao抽出来---->泛型接口
public interface Dao<T>
{
public void add(T t);
public void update(T t);
public void delete(Serializable id);
public T findOne(Serializable id);
public List findAll();
}
Book/Userdao继承该接口
CategoryDao.java 继承了Dao
以至于以后的通用dao(BookDao UserDao Category),都可以继承这种泛型接口!!!
引入hibernate3包--->用来测试而已
新建dao.impl---->BookDaoImpl.java--->实现BookDao
怎么使用hibernate3包?
{
private Session session;
//该类会显示很多继承接口的方法
//在方法里使用bibernate3包提供的方法,类似于获取DBUtil里connection资源的方式
}
但是是否每个实现类都要进行以上同样的操作?
新建dao.impl--->HibernateBassDaoImpl.java--->实现Dao.java 目的为了实现一个通用的模板
{
private Session session = null;
//该类会显示很多继承接口的方法
//操作和BookDaoImpl里一样的操作。。
//不同的是怎么解决泛型T拿取其Class类型?
//首先泛型T拿取不了Class,但是在该类的构造函数里通过接收类再获取就可以
}
通过以上的模板,BookDao就修改继承HibernateBassDaoImpl.java,那么就可以直接调用模板里的泛型方法了。
以后的所有的UserDao/CategoryDao都可以直接继承这些泛型实现类。
测试(BookDao/UserDao/CategoryDao)
通过实现类中的findOne中的语句System.out.println(clz.getName()+"类");来测试泛型模板实现的是否成功
新建Test--->Client.java--->测试
能否再继续抽取(BookDao/UserDao/CategoryDao)中的继承语句,因为子实现类每次要使用时都要传入类对象,不够方便
/*public HibernateBaseDaoImpl(Class clz) {
super();
this.clz = clz;
}*/
但问题是其类类型怎么获取?答:通过dao的实现类去使用反射获得其父接口的Class类型,然后使用方法去获得该接口所传进来的泛型对象类型,以此捕获到要使用到的类类型。
public HibernateBaseDaoImpl() {
Class clzz = this.getClass();//代表的是获得当前这个对象的Class实例,是在Client类中真正所生成的对象BookDaoImpl.class
ParameterizedType pt = (ParameterizedType)clzz.getGenericSuperclass();//通过反射,得到当前类的泛型父类
clz = (Class)pt.getActualTypeArguments()[0];//得到泛型父类的参数类型
}
该构造方法在测试Client类里一调用继承了dao的实现类的实现类就会马上执行。
然后(BookDao/UserDao/CategoryDao)怎么获取HibernateBaseDaoImpl()的方法呢?
那么就Class的文档,让HibernateBaseDaoImpl()得到泛型父类的方法
查到Type getGenericSuperclass()可以做到
Class类实现了Type类接口---Type类接口的子接口ParameterizedType--->该子接口ParameterizedType可以获得<>里实际的类型
在HibernateBassDaoImpl.java类里的源码继续改进
public HibernateBaseDaoImpl
Class clzz = this.getClass();//代表的是获得当前这个对象的Class实例,是在Client类中真正所生成的对象BookDaoImpl.class
ParameterizedType pt = (ParameterizedType)clzz.getGenericSuperclass();//通过反射,得到当前类的泛型父类
clz = (Class)pt.getActualTypeArguments()[0];//得到泛型父类的参数类型
}
继续在Client.java测试具体类是否能被实例化
以上利用了反射泛型的技术
该节课以后做项目非常有用!!!
Dao设计模式:
实验: 项目:day2100generic
BookDao.java
package com.itheima.generic.dao;
import java.io.Serializable;
import java.util.List;
import com.itheima.generic.domain.Book;
public interface BookDao extends Dao<Book> {
/*public void add(Book book);
public void update(Book book);
public void delete(Serializable id);//以后可以放入String,int,double
public Book findOne(Serializable id);
public List findAll();*/
}
UserDao.java
package com.itheima.generic.dao;
import java.io.Serializable;
import java.util.List;
import com.itheima.generic.domain.User;
public interface UserDao extends Dao<User> {
/*public void add(User book);
public void update(User book);
public void delete(Serializable id);//以后可以放入String,int,double
public User findOne(Serializable id);
public List findAll();*/
}
CategoryDao.java
package com.itheima.generic.dao;
import java.io.Serializable;
import java.util.List;
import com.itheima.generic.domain.Category;
public interface CategoryDao extends Dao<Category> {
}
Dao.java
package com.itheima.generic.dao;
import java.io.Serializable;
import java.util.List;
import com.itheima.generic.domain.User;
public interface Dao<T> {
public void add(T t);
public void update(T t);
public void delete(Serializable id);//以后可以放入String,int,double
public T findOne(Serializable id);
public List findAll();
}
HibernateBaseDaoImpl.java
package com.itheima.generic.dao.impl;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.hibernate.Session;
import com.itheima.generic.dao.Dao;
public class HibernateBaseDaoImpl<T> implements Dao<T> {
private Session session=null;
private Class clz;
/*public HibernateBaseDaoImpl(Class clz) {
super();
this.clz = clz;
}*/
public HibernateBaseDaoImpl() {
Class clzz = this.getClass();//代表的是获得当前这个对象的Class实例,是在Client类中真正所生成的对象BookDaoImpl.class
ParameterizedType pt = (ParameterizedType)clzz.getGenericSuperclass();//通过反射,得到当前类的泛型父类
clz = (Class)pt.getActualTypeArguments()[0];//得到泛型父类的参数类型
}
@Override
public void add(T t) {
session.save(t);
}
@Override
public void update(T t) {
session.update(t);
}
@Override
public void delete(Serializable id) {
Object obj =session.get(clz, id);//取一个记录,并且没有延迟加载
session.delete(obj);
}
@Override
public T findOne(Serializable id) {
System.out.println(clz.getName()+"类被实例化了");
return (T)session.get(clz, id);
}
@Override
public List findAll() {
// TODO Auto-generated method stub
return null;
}
}
BookDaoImpl.java
package com.itheima.generic.dao.impl;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Session;
import com.itheima.generic.dao.BookDao;
import com.itheima.generic.domain.Book;
public class BookDaoImpl extends HibernateBaseDaoImpl<Book> implements BookDao {
/*public BookDaoImpl() {
super(Book.class);
}*/
}
UserDaoImpl.java
package com.itheima.generic.dao.impl;
import java.io.Serializable;
import java.util.List;
import com.itheima.generic.dao.UserDao;
import com.itheima.generic.domain.User;
public class UserDaoImpl extends HibernateBaseDaoImpl<User> implements UserDao {
/*public UserDaoImpl() {
super(User.class);
}
*/
}
CategoryDaoImpl.java
package com.itheima.generic.dao.impl;
import com.itheima.generic.dao.CategoryDao;
import com.itheima.generic.domain.Category;
public class CategoryDaoImpl extends HibernateBaseDaoImpl<Category> implements CategoryDao {
/*public CategoryDaoImpl() {
super(Category.class);
}*/
}
Book.java
package com.itheima.generic.domain;
import java.io.Serializable;
public class Book implements Serializable{
}
Category.java
package com.itheima.generic.domain;
import java.io.Serializable;
public class Category implements Serializable{
}
User.java
package com.itheima.generic.domain;
import java.io.Serializable;
public class User implements Serializable {
}
Client.java
package com.itheima.test;
import com.itheima.generic.dao.BookDao;
import com.itheima.generic.dao.CategoryDao;
import com.itheima.generic.dao.impl.BookDaoImpl;
import com.itheima.generic.dao.impl.CategoryDaoImpl;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
//BookDao dao = new BookDaoImpl();
CategoryDao cdao = new CategoryDaoImpl();
//dao.findOne(1);
cdao.findOne(2);
}
}
Dao模式下泛型的引入测试结果:
使用多态机制用Serializable接收Integer等基本类型:
三、注解及反射注解
3、注解及反射注解
(父类)Parent
{
public void m1(){
System.out.prinltn("m1");
}
public void m2(){
System.out.println("m2");
}
public void lllll(){
System.out.println("dddd");
}
}
Child
{
@Override //注解:JDK1.5+
public void m1(){
super.m1();
}
@Override
public void lllll(){//如果看错,父类的方法是11111不是llll,那么@Overrade就起作用了,起方法名检测的作用
System.out.println("ccccc");//重写了父类方法
}
@Override //注解,实现类中的方法 JDK1.6+
public void m4(){
}
@Deprecated
public void m1111111111111(){//方法过时
}
@SuppressWarnings({"unused","rawtypes","unchecked"})
public void m5(){
int i = 0;
List list = new ArrayList();
list.add(i);
}
}
(接口)ParentInterface.java,让Parent实现它
{
public static m4();
}
实验:项目:day2101annotation:com.itheima.test
项目架构:
Child.java
package com.itheima.test;
import java.util.ArrayList;
import java.util.List;
public class Child extends Parent implements ParentInterface{
@Override //注解 JDK1.5+
public void m1() {
super.m1();
}
@Override
public void lllll1(){
System.out.println("ccccc");
}
@Override //注解,实现接口中的方法 JDK1.6+
public void m4() {
}
@Deprecated
public void mlllllllllllllllllllllllll(){
}
@SuppressWarnings({ "all" })//@SuppressWarnings({"unused","rawtypes","unchecked"})
public void m5(){
int i=0;
List list = new ArrayList();
list.add(i);
}
}
Parent.java
package com.itheima.test;
public class Parent {
public void m1(){
System.out.println("m1");
}
public void m2(){
System.out.println("m2");
}
public void lllll1(){
System.out.println("dddddddd");
}
}
ParentInterface.java
package com.itheima.test;
public interface ParentInterface {
public void m4();
}
这些是JDK所提供的注解 那么自己如何定义注解呢?
MyAnn1.java //public @interface MyAnn1 定义注解用@interface ,注解其实也是一个类
{
public int age() default 18;//定义了一个属性 default代表默认值
//public Date birthday();注解类不支持Date类型
public String name();
public MyAnn2[] address();//MyAnn1又放了一个注解数组
public String name();
public Gender sex() default Gender.MALE;
}
MyAnn2.java
{
public String province();
public String home();
}
enum Gender
{
}
MyAnnTest
{ //要让注解起作用,就必须给注解以灵魂,要给灵魂就要反射注解
@MyAnn1(name="山谷",age=20,sex=Gender.FEMALE,birthday=java.util.Date.class,
address={@MyAnn2(province="广东省",home="凌云大厦11层101房间"),@MyAnn2(province="广东省",home="凌云大厦11层101房间")})
public void m1(){
}
@MyAnn2(province="广东省",home="凌云大厦11层101房间");
public void m2(){
}
}
项目:day2101annotation:com.itheima.anno1
MyAnn1.java
package com.itheima.anno1;
import java.util.Date;
//定义注解用@interface ,注解其实也是一个类
public @interface MyAnn1 {
public int age() default 18; //定义了一个属性 default 代表默认值
//Invalid type Date for the annotation attribute MyAnn1.birthday;
//only primitive type, String, Class, annotation, enumeration are permitted or 1-dimensional arrays thereof
public Class birthday();//public Date birthday();注解类不支持Date类型
public String name() ;
public MyAnn2[] address();
public Gender sex() default Gender.MALE;
}
MyAnn2.java
package com.itheima.anno1;
public @interface MyAnn2 {
public String province();
public String home();
}
Gender.java
package com.itheima.anno1;
public enum Gender {
MALE,FEMALE;
}
MyAnnTest.java
package com.itheima.anno1;
//要让注解起作用,就必须给注解以灵魂,要给灵魂就要反射注解
public class MyAnnTest {
@MyAnn1(name="尚也",age=20,sex=Gender.FEMALE,birthday=java.util.Date.class,
address={@MyAnn2(province="广东省",home="凌云大厦11层101房间"),
@MyAnn2(province="广东省",home="凌云大厦11层101房间")
}
)
public void m1(){
}
@MyAnn2(province="广东省",home="凌云大厦11层101房间")
public void m2(){
}
}
四、反射注解及单元测试原理
怎么让注解起作用
类的状态
(Annotation)MyTest
{
public long timeout() default 0;
}
UserDaoImpl.java
{
@MyTest(timeout=100000)
public void m1(){
System.out.println("m1");
}
public void m2(){
System.out.println("m2");
}
}
MyTestRunner.java
{
main(){
UserDaoImpl dao = new UserDaoImpl();
Class clz = dao.getClass();//得到dao对象中的ClassShiite
Method mm [] = clz.getDeclareMethods();//得到dao对象中包含的所有方法
//这样我们就不用关注源类中有什么方法
//接着我们需要想知道哪些方法上面有注解?
for (itn i = 0 ;i<mm.lentgh ;i++ )
{
method m = mm[i];
//关键步骤:就是要判断方法上面有没有注解
//查文档寻找方法:Method--->isAnnotationPresent()-->返回boolean
if(m.isAnnotationPresent(MyTest.calss)){
System.out.println(m.getName()+"方法上面有注解");
}
else{
System.out.println(m.getName()+"方法上面没有注解");
}
//但测试都输出没有注解。为什么?
//查看UserDaoImpl.class的字节码
//发现有注解
//通过类的状态发现,我们需要内存中也加载了注解
//这样注解才能使用
}
}
}
原来的
//元注解:用于描述注解的注解
@Retention(RetentionPolicy.RUNTIME)//代表在运行时也将注解保留住
@Target(ElementType.Method)//代表出现该注解的位置
anno2-->(Annotation)MyTest
{
public long timeout() default 0;
}
设置完以上的代码后
原来的
MyTestRunner.java
{
main(){
UserDaoImpl dao = new UserDaoImpl();
Class clz = dao.getClass();//得到dao对象中的ClassShiite
Method mm [] = clz.getDeclareMethods();//得到dao对象中包含的所有方法
//这样我们就不用关注源类中有什么方法
//接着我们需要想知道哪些方法上面有注解?
for (itn i = 0 ;i<mm.lentgh ;i++ )
{
method m = mm[i];
//关键步骤:就是要判断方法上面有没有注解
if(m.isAnnotationPresent(MyTest.calss)){
MyTest mt = m.getAnnotation(MyTest.class);//得到方法上面的注解对象
long timeout = mt.timeout();//取出注解的属性:100000(在UserDaoImpl里)
long ntime = System.nanoTime();
m.invoke(dao,args);//反射定位到了标有注解的方法,然后方法进行反射--->执行
long execTime = System.nanoTime()-ntime;//这是真正执行方法所用时间
if(execTime>timeout){
System.out.println(m.getName()+"方法执行了,但超时了"+execTime);
}
}else{
System.out.println(m.getName()+"方法上面没有注解");
}
}
}
}
测试,通过修改UserDaoImpl里的timeout里的值!!!来测试
总的逻辑:首先新建没有元注解描述的注解类,给该注解类设定属性,然后通过新建类在类里的方法标上注解,但是发现注解什么也没发生,原因是注解类没有元注解描述,在加了
元注解后测试时发现还是没有反应,那是因为没有用反射给注释类注入灵魂(即需要执行的代码)
实验项目:day2101annotation:com.itheima.anno2
Limit.properties
UserDaoImpl.java
package com.itheima.anno2;
public class UserDaoImpl {
@MyTest(timeout=200000)
public void m1(){
System.out.println("m1");
}
public void m2(){
System.out.println("m2");
}
}
MyTest.java
package com.itheima.anno2;
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 MyTest {
public long timeout() default 0;
}
MyTestRunner.java
package com.itheima.anno2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyTestRunner {
//利用反射给MyTest注释类注入灵魂
public static void main(String[] args) {
UserDaoImpl dao = new UserDaoImpl();
Class clz = dao.getClass();//得到dao对象中的Class实例
Method mm [] = clz.getDeclaredMethods();//得到dao对象中包含的所有方法
/*for (int i = 0; i < mm.length; i++) {
Method m = mm[i];
//关键步骤:就是要判断方法上面有没有注解 isAnnotationPresent
if(m.isAnnotationPresent(MyTest.class)){
System.out.println(m.getName()+"方法上面有注解");
}else{
System.out.println(m.getName()+"方法上面没有注解");
}
}*/
for (int i = 0; i < mm.length; i++) {
Method m = mm[i];
//关键步骤:就是要判断方法上面有没有注解 isAnnotationPresent
if(m.isAnnotationPresent(MyTest.class)){
MyTest mt = m.getAnnotation(MyTest.class);//得到方法上面的注解对象
long timeout = mt.timeout();//取出注解的属性
long ntime = System.nanoTime();
try {
m.invoke(dao, args);//反射执行方法里的内容
} catch (Exception e) {
e.printStackTrace();
}
long execTime = System.nanoTime()-ntime;//这是真正执行方法所用时间
if(execTime>timeout){
System.out.println("方法执行了,但超时了"+execTime);
}
}else{
System.out.println(m.getName()+"方法上面没有注解");
}
}
}
}
测试结果:
五、反射注解及单元测试原理:实例
Account.java
{
private int balance = 10000;//余额
public void drawMoney(int money){
if(money>balance){
throw new RuntimeException("余额不足!");
}
//最高限额
//新建Limit.Properties文件
ResourceBundle rb = ResourceBundle.getBundle("Limit");//获取Properties的新方法
String limit = rb.getString("Limit");
if(money>Integer.parseInt(limit)){
throw new RuntimeException("取款超限");
}
//支取
balance = balance-money;
System.out.println("取款成功,当前余额是:"+balance);
}
}
新建Limit.Properties文件 Limit-3000
Client.java
{
main(){
Account ac = new Account();
//ac.drawMoney(1000);//取款成功,当前余额是:9000
//ac.drawMoney(3000);//取款成功,当前余额是:7000
ac.drawMoney(5000);
}
}//这是以前的做法,使用配置文件配置项目。
//现在用注解实现以上方法
利用注解取代配置文件,这就是以后的项目不再使用xml来配置文件,而是使用注解来代替!!
public @interface Limit{
public int value();//如果属性名为value,在赋值时没有指定给哪个属性时,
}
Account.java
{
private int balance = 10000;//余额
public void drawMoney(int money){
if(money>balance){
throw new RuntimeException("余额不足!");
}
//最高限额
//新建Limit.Properties文件
// ResourceBundle rb = ResourceBundle.getBundle("Limit");//获取Properties的新方法
// String limit = rb.getString("Limit");
// if(money>Integer.parseInt(limit)){
// throw new RuntimeException("取款超限");
// }
//支取
balance = balance-money;
System.out.println("取款成功,当前余额是:"+balance);
}
}
Client.java
{
main(){
Account ac = new Account();
//ac.drawMoney(1000);//取款成功,当前余额是:9000
//ac.drawMoney(3000);//取款成功,当前余额是:7000
ac.drawMoney(5000);
}
}
实验项目:day2101annotation:com.itheima.anno3
Limit.java
package com.itheima.anno3;
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 Limit {
public int value() ;//如果属性名为value,在赋值时没有指定给哪个属性,就是给value赋值
}
Account.java
package com.itheima.anno3;
import java.lang.reflect.Method;
public class Account {
private int balance=10000;
@Limit(3000)
public void drawMoney(int money){
if(money>balance){
throw new RuntimeException("余额不足!");
}
/*//最高限额
ResourceBundle rb = ResourceBundle.getBundle("Limit");
String limit = rb.getString("Limit");
if(money>Integer.parseInt(limit)){
throw new RuntimeException("取款金额超限");
}
*/
//注解实现
Limit limit =null;
try {
Method drawMoney = Account.class.getMethod("drawMoney", int.class);
limit = drawMoney.getAnnotation(Limit.class);
} catch (Exception e) {
e.printStackTrace();
}
if(limit!=null){
int limitMoney = limit.value();
if(money>limitMoney){
throw new RuntimeException("取款金额超限");
}
}
//支取
balance = balance-money;
System.out.println("取款成功,当前余额是:"+balance);
}
}
Client.java
package com.itheima.anno3;
public class Client {
public static void main(String[] args) {
Account ac = new Account();
//ac.drawMoney(1000);
ac.drawMoney(3000);
//ac.drawMoney(5000);
}
}
测试结果:
问题:用注解类代替配置文件Properties的优势在哪里?耦合性能解决? 答:通过调用在方法上做出了@xxxx(xxx=xxx)注释时,提取注解里的属性,通过反射拿到注解属性值,然后通过操作对属性值进行描述(添加操作代码,返回相应的结果!!例如在
Account.java里的获取注解值并赋予灵魂的例子)
六、Servlet注解配置
4、Servlet3.0
准备: 要使用该特性,tomcat要换成7.0以上 老师拷在资料里了。 资料库
版本
参考Servlet3.0新特性的文档《总结servlet3.0新特性资料》和全英文版的说明书
Servlet3.0新特性概览
1.Servlet、Filter、Listener无需在web.xml中进行配置,可以通过Annotation进行配置;
2.模块化编程,即将各个Servlet模块化,将配置文件也分开配置。
3.Servlet异步处理,应对复杂业务处理;
4.异步Listener,对于异步处理的创建、完成等进行监听;
5. 文件上传API简化;
把day2102servlet3.0的web.xml删除,本项目就是为了用注释类来替代xml配置文件
用servlet3.0时,JDK版本也要提升JDK1.6
ServletDemo1--->取消xml配置
@WebServlet(urlPatterns={"/servlet/ServletDemo1","/servlet/a.jpg"})//看tomcat的源码去查看该注解里的属性值
//把路径写成/servlet/a.jpg,在IE网页上输入这样的结尾,可以欺骗客户。
ServletDemo1
{
doGet(){
sysn("ServletDemo1执行了");
}
}
启动Tomcat测试:输入http://localhost:8080/day2102servlet3/servlet/a.jpg
http://localhost:8080/day2102servlet3/servlet/servlet/ServletDemo1
@WebServlet(urlPatterns={"/servlet/ServletDemo1","/servlet/a.jpg"},
initParams={@WebInitParam(name="encoding",value="UTF-8"),
@WebInitParam(name="aj",value="cj")
}
)
ServletDemo1
{
doGet(){
sysn("ServletDemo1执行了");
}
}
是否需要早上反射那样提取注解属性(配置文件属性)吗?
不可能!
使用以前用来提取xml的ServletConfig接口中的getInitParameter("");来提取
@WebServlet(urlPatterns={"/servlet/ServletDemo1","/servlet/a.jpg"},
initParams={@WebInitParam(name="encoding",value="UTF-8"),
@WebInitParam(name="aj",value="cj")
}
)
ServletDemo1
{
doGet(){
sysn("ServletDemo1执行了");
}
//ServletConfig接口中的getInitParameter("");
ServletConfig config = getServletConfig();
//@WebInitParam(name="encoding",value="UTF-8"),
//@WebInitParam(name="aj",value="cj")
Enumeration<String> enumss = config.getInitParameterNames();
while(){}--->去属性值!!
}
执行ServletDemo1去获取!!!
老师看法:这样有优点,但是配置文件全部都在源代码里,太灵活,不想XML那样的更好的对属性的管理,有利有弊
但这是趋势。
注解可以完全代替xml
适合小项目
可以综合使用,但是对于维护来说,很困难。
这种机制不符合软件工程的思想。
实验项目:day2102servlet3.0
ServletDemo1.java
package com.itheima.servlet;
import java.io.IOException;
import java.util.Enumeration;
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={"/servlet/ServletDemo1","/servlet/a.jpg"},
initParams={@WebInitParam(name="encoding",value="UTF-8"),
@WebInitParam(name="AJ",value="CJ")
}
)
public class ServletDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("ServletDemo1执行了");
//用ServletConfig接口中的getInitParameter("");
ServletConfig config = getServletConfig();
//@WebInitParam(name="encoding",value="UTF-8"),
// @WebInitParam(name="AJ",value="CJ")
Enumeration<String> enumss = config.getInitParameterNames();
while(enumss.hasMoreElements()){
String paramName = enumss.nextElement();
String paramValue = config.getInitParameter(paramName);
System.out.println(paramName+","+paramValue);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
FilterDemo1.java
package com.itheima.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;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebListener;
@WebFilter(urlPatterns={"/*"},initParams={@WebInitParam(name="encoding",value="GBk")}
)
public class FilterDemo1 implements Filter {
private FilterConfig config ;
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("FilterDemo1 初始化了");
config = filterConfig;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String encoding = config.getInitParameter("encoding");
System.out.println("FilterDemo1放行前,"+encoding);
chain.doFilter(request, response);//放行
System.out.println("FilterDemo1放行后");
}
public void destroy() {
System.out.println("FilterDemo1 销毁了");
}
}
GFilterDemo2.java
package com.itheima.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 GFilterDemo2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("GFilterDemo2放行前");
chain.doFilter(request, response);
System.out.println("GFilterDemo2放行后");
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
测试1:
测试2:
七、Servlet3.0实现文件上传
查看servlet3.0包里的文档
Part接口
发现Part接口类
void write(java.lang.String fileName)
A convenience method to write this uploaded item to disk.
其实上传的方法用这个代码可以实现。方便
那么怎么获取Part呢?
继续看HttpServletRequest类
发现 Part getPart(java.lang.String name)
Gets the Part with the given name.
文件上传页面:复制之前的项目页面index.jsp
之前的Commons-FileUpload-xxx.jar apach公司已经将文件上传在Tomcat7中已经整合了。
day21_02_servlet3
servlet--->FileUploadServlet3
{
doGet(){
request.setCharacterEncoding("UTF-8");
1、得到普通字段
String username=request.getParameter("username");
2、得到上传字段 input type="file" name="f1">
request.getPart("f1").write(getServletContext().getRealPath("/WEB-INF/files")+File.separator+i+".jpg");
//request.getPart("f1")已经获得了从jsp上传的文件了。强大!!!
}
}
以上代码靠谱吗?在没有配置文件之前怎么能靠谱?
这Part接口类是servlet3.0的东西。那么我们就需要配置信息啊,所以用今天学的使用注解来配置servlet3.0的信息
Part接口类
@WebSerlvet("/servlet/FileUploadServlet3")
@MultipartConfig() //必须加,否则不能实现文件上传
FileUploadServlet3
{
doGet(){
request.setCharacterEncoding("UTF-8");
1、得到普通字段
String username=request.getParameter("username");
2、得到上传字段 input type="file" name="f1">
request.getPart("f1").write(getServletContext().getRealPath("/WEB-INF/files")+File.separator+i+".jpg");
//request.getPart("f1")已经获得了从jsp上传的文件了。强大!!!
}
}
测试访问index.jsp上传。
FileUploadServlet3.java
package com.itheima.servlet;
import java.io.File;
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;
@WebServlet("/servlet/FileUploadServlet3")
@MultipartConfig() //必须加,否则不能实现文件上传
//Commons-FileUpload-xxx.jar Apach公司已经将文件上传在Tomacat7+中已经整合了
public class FileUploadServlet3 extends HttpServlet {
private int i=1;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTf-8");
//1.得到普通字段
String username = request.getParameter("username");
//2.得到上传字段 input type="file" name="f1">
request.getPart("f1").write(getServletContext().getRealPath("/WEB-INF/files")+File.separator+i+".jpg");
i++;
response.getWriter().write(username);
response.getWriter().write("文件上传成功");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="http://blog.163.com/faith_yee/blog/<%=basePath%>">
<title>My JSP ‘index.jsp‘ starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="http://blog.163.com/faith_yee/blog/styles.css">
-->
</head>
<!-- 文件上传的三个前提条件:
1.enctype="multipart/form-data"
2.method="post"
3.必须有<input type="file" name="f1">
-->
<body>
<form enctype="multipart/form-data" action="${pageContext.request.contextPath }/servlet/FileUploadServlet3" method="post">
姓名:<input type="text" name="username"/><br>
靓照1:<input type="file" name="f1"><br>
<input type="submit" value="上传"/><br>
</form>
</body>
</html>
测试1:
测试2:
测试3:
八、Servlet3.0实现过滤器配置
@WebFilter(urlPatterns={"/*"},initParams={@WebInitParam(name="encoding",value="GBK")})
FilterDemo1 implements Filter
{ private Filteronfig config;
init()
{
config= filterConfig;
sysn("初始化了");
}
doFIlter(){
String encoding = config......
sysn("放行前"+encoding);
chain.doFilter(...);//放行
sysn("放行后");
}
}
测试index.jsp
那么多个Filter的顺序呢??
失望的是没有相关的属性定义Filter的顺序。
那么怎么办?
filter-->AFilterDemo2
在这里只能看Filter的名字来排序了
测试index.jsp
九、Servlet3.0实现监听器的配置
MyServletContextListener.java
@WebListener()//什么也不用写,和以前xml一致,就相当于注册了
MyServletContextListener
{
ContextInitialized(){
sysn("ServletContext被初始化了");
}
}
day2102servlet3:MyServletContextListener.java
package com.itheima.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext被初始化了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext被销毁了");
}
}
测试1:
测试2:
十、异步任务监听器
Servlet在MVC中作为控制器,控制器负责分发任务给MODEL完成,然后把结果交给JSP显示;
而如果有许多MODEL,其中有一个MODEL处理时间很长,则会导致整个页面的显示很慢;
异步处理关键点:将复杂业务处理另外开一个线程,而Servlet将执行好的业务先送往jsp输出,等到耗时业务做完后再送往JSP页面;
一句话:先显示一部分,再显示一部分;
查看总结Servlet3.0新特性资料
新建servlet-->
@WebServlet(urlPattern={"/servlet/AsyncServletDemo4"},asyncSupported=true)
AsncServletDemo4.java
{
doGet(){
打印...(上部分(马上显示)、中部分(一下显示不出)、下部分(一下显示出来))
out.flush();
}
创建一个内部类(线程类)
}
测试:AsyncServletDemo4.java
由于例子被Filter过滤干扰了,移动例子到项目Day2103asynctask
还有一个新特性:异步监听器:算起来就有9个监听器了
在AsyncServletDemo4.java有演示
实验项目:day2103asynctask
项目架构:
01.jsp
<%@ page language="java" import="java.util.*" session="false" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<%
out.write("耗时的业务");
%>
</body>
</html>
AsyncServletDemo4.java
package com.itheima.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns={"/servlet/AsyncServletDemo4"},asyncSupported=true)
public class AsyncServletDemo4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("GBK");
response.setContentType("text/html;charset=GBK");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("====页面开始====<hr />");
AsyncContext ac = request.startAsync();
ac.addListener(new MyAsyncListener());
ac.setTimeout(30*1000);
ac.start(new MyRunnable(ac));
out.println("====页面结束====<hr />");
out.println("</body>");
out.println("</html>");
out.flush();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
public class MyRunnable implements Runnable {
private AsyncContext ac ;
public MyRunnable(AsyncContext ac){
this.ac = ac;
}
public void run() {
try {
Thread.sleep(10*1000);
ac.dispatch("/01.jsp");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MyAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("异步任务执行完成");
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
// TODO Auto-generated method stub
}
@Override
public void onError(AsyncEvent event) throws IOException {
// TODO Auto-generated method stub
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
// TODO Auto-generated method stub
}
}
}
测试1:
2
3
4
5
十一、日志记录原理
Log4J:Log4J是Apache的一个开放源代码项目,它是一个日志操作软件包。通过使用Log4J,可以指定日志信息输出的多种目的地,比如控制台、文件等;还可以控制每一条日志的输出格式。通过定义日志信息的级别,能够非常细致地控制日志的输出。 以上的这些功能都可以通过一个配置文件来灵活地进行配置,而不需要修改应用程序的代码.
为什么需要Log4J:日志的作用:
监视代码中变量的变化情况,把数据周期性地记录到文件中供其他应用进行统计分析工作跟踪代码运行时轨迹,作为日后审计的依据。
担当集成开发环境中的调试器的作用,向文件或控制台打印代码的调试信息。
要在程序中输出日志,最普通的做法就是在代码中嵌入许多的打印语句(System.out.println()),这些打印语句可以把日志输出到控制台或文件中。------做法不好
导包-->log4j-1.2.17.jar
这功能非常有用!!例如银行需要
1、日志--->永久保存在磁盘上
记录登录信息
记录交易信息,以实现数据恢复
2、开发时引入log4j.jar
3、配置文件log4j.properties--->找别的软件复制过来参考
新建log4j.properties
test--->
log4jTest
{
main(){
Logger log = Logger.getLogger("");
log.debug("用户登录了");
log.info("info级别");
log.warn("warn级别");
log.error("error级别");
log.fatal("fatal级别");//从上往下,级别由低到高
//在properties里调节级别,输出
}
}
不断改变级别去测试该类
要不要写文件取决于properties文件
log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=debug, CONSOLE,LOGFILE
#log4j.rootCategory=INFO, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:/axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.Threshold=DEBUG
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
实验项目:day2104log4j
项目架构:
log4j.properties
Log4jTest.java
package com.itheima.test;
import org.apache.log4j.Logger;
public class Log4jTest {
public static void main(String[] args) {
Logger log = Logger.getLogger("bookStore");
log.debug("用户登录了");
// log.info("info级别");
// log.warn("warn级别");
// log.error("error级别");
// log.fatal("fatal级别"); //从上往下,级别由低到高
}
}
测试结果: