说明
因为是个人复习java的总结,所以结构稍显杂乱,有些语句过于口语化.
心得后面会是部分的Filter内容,基本内容都有讲解,但是还有部分内容没有继续深入.
这几天静下来敲了几天代码,做了一个功能不大完善的网站,算是对上面学习的内容的总结.
可以说这几天静下心来将学过的东西用一下是很有用的,我一直觉得学一部分然后去练一部分,根本没办法体会这部分学的有什么用.
分享一下这几天做网站的一些心得:
首先很重要,代码很多错误真的就出在细微处,就一不留神就出错了.全凭脑子构思是很容易就忘记思路的.这几天学会要先构思好整体的思路再去做.可以注释先写好,然后开始整体的完善.另外对于一些类,可能需要调用再底层一些的操作,但是那些操作还没写,这时候不应该去完善下面的操作,很容易把思路弄乱.要写就把目前写的类写完,再去实现下面的一些细节.先把一层的思路理清才能更好的去深入的解决问题,不要怕下面实现之后发现上层的思路有问题,那样再去改的时候思路更加明确.
其次,真的不能烦躁,出现问题仔细看错误提示,再慢慢去排查.一看错误认为自己知道怎么改了,着急去改是没有用的,很可能越改越错.先理清错误,再动手.像网站的一些编写,因为经常使用request,url中的值&和=很容易出错,还有就是request转送之后很可能多出一些不必要的参数,所以转送和重定向都应该根据情况使用.
然后就是关于一些写网站时候的感悟,一部分是关于整体的思想的,一部分是关于细节的内容的.
首先就是对于整体三层架构的一些思考.之前一直觉得这么分稍显繁琐,但是实际上手之后就发现,其实这样写真的很有必要.而且对于service层的难写有了深刻的思考.对于一个网站肯定有很多数据,这些数据肯定又有相同的一些操作,比如说增删改查.这些数据肯定毋庸置疑要封装成实体类,然后进行操作,这样做很方便.刚开始按着这个思路,对于不同的实体类数据封装了不同的Servlet,不同的Service,那么不同的service又会对应到不同的dao中,但是明显这是不合理的.对于不同数据肯定要进行集成,结果我将servlet集成了,其中数据不处理,交给service处理,然后service也集成了,专门处理数据,然后dao也集成了,不管数据,只操作数据.废了很大的功夫,在service对数据进行处理.然后我就醒悟了,发现问题非常严重,但是这时候我已经没法去解决了,因为代码耦合性太高了.
其实上面我走上了两条极端,不知道有没有人和我有一样的体悟,发现上面的问题.刚开始的时候,代码的耦合性很低,但是代码的重复部分很多,所以太繁琐,想着都是进行一样操作,可以集成.然后集成之后,将数据的代码全都集成到了一个模块,虽然之后如果想增加一些实体类,或者删除,十分方便,连Servlet代码都不用改,直接把这个数据从数据库去了,删了前端就好了.但是如果想对这个功能进行一些修改,那工作量太大了,从底层到展示全部都关联在一起,都需要去改变.
其实写到一半的时候我就意识到问题,但是我还是硬着头皮写下去了,全为长个记性,也为了印证自己的想法.就造成了一团糟的结果.
再谈一下最后认为比较合适的方式,首先是Servlet不应该过于集成,因为这部分和展示界面紧紧相连,过于集成的结果就是不同页面需要传递不同参数,这些数据之间相互影响,特别是url的设置会一团糟.如果改动其中一点,很可能导致的是大批的展示界面出现问题,因为传递的参数最终都会对底层的操作进行影响.
至于Service层可以进行集成,一来简化代码,二来这里不做实质性的一些操作,只是进行一些数据的转换,达到数据的统一,那么这里简化代码但是不影响上下的功能,这就很理想,但是也很难写.这里说一下,用反射来实现不同数据的转换真的方便,转换写的好甚至dao中就能很方便的进行操作,不用考虑数据.如果真的对于不同类型很难传递或者准确的向上向下转型,可以借助list,让底层先操作,再返回service解决.
最后就是dao中操作得留神,像数据库操作,先验证一下,不然没测试几次,数据变得乱七八糟的.
还有在我错误的集成Servlet的时候,发现header中的refer真的好用,如果对于一些前端界面的命名和数据都是符合规范的话,在Servlet集成的情况下可以减少不必要的参数传递造成的麻烦.但是说实话这样给我用的有点偏,不建议跟我一样使用.
下面继续学习还会对网站进行一些补充,毕竟练手,顺便当个课设…
Filter
其实就相当于在web的数据相互传递中设置了一个卡口,对于部分或者全部的请求或者响应进行一些统一的操作的东西.
实际使用可能是比如说编码的设置,敏感字符的过滤,或者登陆访问限制,其实可以做到一些保护网站的效果.
使用流程的话,不是说需要网站先访问这个过滤器,而是过滤器主动去拦截一些访问路径上的请求响应.
所以需要定义类实现接口Filter,然后重写方法并设置拦截路径进行一些操作.
使用的是javax.servlet
下的Filter
整体的代码结构是像下面的
@WebFilter("/*")
public class FilterDemo implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException{
//拦截放行操作
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {}
}
那么从上至下理解,首先就是过滤器的配置,因为肯定不能一条一条写在代码里,那么就两种常用的方式,使用注解配置或者文件配置.
注解配置
@WebFilter()
其中最主要的就是urlPatterns,也就是拦截路径,当然可以通过唯一的value来配置urlPatterns.也就是上面代码中的方式,否则是要用urlPatterns=""
来配置的,忘了可以再看一下注解配置的一些概念
使用web.xml配置过滤器
<filter>
<filter-name>demo</filter-name>
<filter-class>filter.FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>demo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
了解一下,还是注解配置会比较多,比较方便,当然为了web.xml也可能会用到,注意写的url是拦截的路径.
过滤器的执行流程
其实很好理解,就是随着请求进入服务器被拦截,拦截之后再访问服务器中想要请求的资源,然后再返回,返回又被过滤器拦住,再回到响应前往的资源.
所以过滤器中一般先对request进行一些增强,然后再放行前往服务器中的资源,或者直接拦截这个请求.对于响应也可以进行一些增强然后再返回到响应的页面.
Filter的生命周期
Filter会在服务器启动的时候创建,那么很显然创建的时候会调用其中的init()也就是初始化方法.
然后每次请求被拦截的时候,都会执行Filter中的doFilter()
方法.
至于destroy()
方法则会在服务器正常关闭的时候调用,进行资源的一些销毁
这也和之前Servlet中的destroy()
区分一下,Servlet相当于是随着访问而创建,随着Servlet的不使用,或者过多请求而销毁Servlet中的一个,不是说完全随着服务器关闭而销毁,而Filter则是随着服务器开启和关闭,毕竟服务器开启之后就会开始接收请求.
Filter拦截路径
- 具体资源路径 /index.jsp 这样只有访问index.jsp的时候才会拦截,使用比较少,因为Filter本来就是为了通用的拦截.
- 拦截目录 /user/* 这样在访问某目录下的资源的时候会进行拦截
- 拦截后缀名 *.jsp访问后缀名带有jsp的时候会进行拦截
- 拦截所有 /*
Filter拦截方式
其实就是对于Filter的拦截设置在哪的问题,访问服务器的资源可以是网页的请求,也可以是转发.这里设置的也就是对于其中那种方式进行拦截.
同样配置拦截方式分注解和web.xml
web.xml就在xml中设置<dispatcher></dispatcher>
标签就好了
注解配置的时候需要配置dispatcherTypes这个属性,同时注意配置时urlPatterns或者value就需要写出来配置,不能直接写/*
了.
dispatcherTypes具体可以设置五种值:
REQUEST
默认值,字面意思,就是浏览器请求资源的拦截方式
FORWARD
转发访问资源的拦截方式,也就是只拦截内部的转发
INCLUDE
包含访问方式
ERROR
错误跳转资源
ASYNC
异步访问资源
注意上面的配置是可以同时配置的,注释中可以传入数组{DispatcherType.Forward,...}
,下面三个属性具体的内容以后再深入了解
需要注意一个问题,下面设置的拦截方式并不会对上面的拦截资源造成影响,也就是说拦截方式只是增加一些拦截,而对于资源的拦截的设置是不管你请求方式的.比如对于一个Servlet转发请求了,但是请求拦截设置的是/*那么不论你拦截方式设置什么都会被拦截,因为拦截访问的资源达到了拦截的要求.
过滤器链
也就是说过滤器是可以重复设置的,那么主要就是考虑多个过滤器之间的先后顺序问题
首先毋庸置疑的是,过滤器就相当于卡点,那么请求时先执行的在响应时肯定后执行.
那么就要考虑不同过滤器之间的先后顺序的问题,这就分为根据web.xml
配置的和注解配置的Filter.
注解配置的Filter是根据类名比较的,也就是说逐个比较字符串的字符,从头开始比较,谁ascll先小就先执行.不考虑字符串长度.
也就是说filter6
会在filter17
之后执行,因为逐个字符比较中1的ascll首先小于6的ascll.
简单的登陆访问过滤案例
部分资源限制了需要登陆之后才能访问,如果没有登陆就跳转到登陆界面
当然这个案例是基于已经做好了登陆的情况下做的.
分析:首先需要在登陆的Servlet中将登陆的状态存到Session中,因为用户的登陆信息肯定是不能放在request或者cookie中,这样会被用户篡改,所以需要将用户的登陆信息放在Session中.我放的是用户的实体类,方便后续加登陆后的权限分类之类的内容.
然后就需要创建一个Filter,设置需要拦截的路径,然后通过request获取其中的用户信息,然后判断,通过就放行,否则拦截并转送登陆界面.
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//因为使用的一般都是http协议,所以直接用HttpServletRequest进行强转
HttpServletRequest request = (HttpServletRequest) req;
//获取请求的路径
String uri = request.getRequestURI();
//判断特定路径通过
if(uri.contains("/login.jsp")){
chain.doFilter(req, resp);
}else {
//获取session中的user,判断是否为登陆状态
Object user = request.getSession().getAttribute("user");
if (user != null){
chain.doFilter(req, resp);
}else {
request.setAttribute("login_msg","请先登陆再访问");
request.getRequestDispatcher(request.getContextPath()+"/front/login.jsp").forward(request,resp);
}
}
}
这里只贴一下doFilter()
中的内容,设置用户的session就不写了.
需要注意其中的req和resp是ServletRequest和ServletResponse接口的,所以使用的时候我们一般会转换成Http的对象进行操作.
上面写的代码是在考虑到登陆界面包含在限制访问资源中的时候,所以需要获取uri进行判断,避免登陆界面都被限制了,陷入逻辑的矛盾.同时需要注意登陆界面中很可能需要访问其他的资源,如果这些资源也在限制范围中,需要也包括进去.
像我限制访问的部分已经和展示的公共界面分离,所以其实不需要获取uri,只要获取Session然后进行判断就可以了.
但是其实上面的案例是很简单的应用,过滤器还应该可以进行数据的一些增强或者过滤.之后还会有再深入一点的应用,但是牵扯到其他的一些内容.
如有错误欢迎读者批评指正!!