一、静态代理
静态代理的主要特点是代理类和被代理类通常具有相同的接口,这样客户端代码可以透明地使用代理类代替被代理类。
首先我们建立一个接口Shopping,在里面定义一个shopping方法。然后创建两个类EasyA和Proxy类来继承Shopping类,并重写里面的方法。将EasyA类作为被代理类,Proxy类作为代理类,我们只需要调用代理类,让它帮我们封装被代理类。
在代理类中,我们声明一个被代理类的类型变量,并将他作为参数定义构造函数,在代理类中重写的方法不仅调用了被代理类的对象的方法,还在它的前后写上了日志。
在主函数中,我们首先声明一个被代理类的对象,然后将其作为参数传入代理类,然后执行主函数,代理类调用被代理类的方法。
接口代码:
package com.easy.staticproxy;
public interface Shopping {
void shopping();
}
被代理类代码:
package com.easy.staticproxy;
public class EasyA implements Shopping{
@Override
public void shopping() {
System.out.println("去购物");
}
}
代理类代码:
package com.easy.staticproxy;
public class Proxy implements Shopping{
Shopping s;
public Proxy(Shopping s){
this.s=s;
}
@Override
public void shopping() {
System.out.println("-----1111");
s.shopping();
System.out.println("-----2222");
}
}
主函数代码:
package com.easy.staticproxy;
public class Factory {
public static Shopping getShopping(){
EasyA a=new EasyA();
Shopping s=new Proxy(a);
return s;
}
public static void main(String[] args) {
Shopping shopping=getShopping();
shopping.shopping();
}
}
二、动态代理
通过动态生成一个代理对象,这个代理对象通过getProxy方法获取了参数所实现的所有接口,比如被代理类实现的EasyInterface接口/Shopping接口。当shopping方法被调用时,会执行EasyInvocationhandler类中的invoke方法。在invoke方法中,首先输出 "-----执行方法之前添加的业务"。然后调用实际的EasyA对象的shopping方法。最后输出 "执行方法之后的处理"。
接口:
package com.easy.staticproxy.dynamic.jdk;
public interface EasyInterface {
void easy();
}
被代理类:
package com.easy.staticproxy.dynamic.jdk;
public class EasyObj implements EasyInterface{
@Override
public void easy() {
System.out.println("---正常业务逻辑");
}
}
package com.easy.staticproxy.dynamic.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class EasyInvocationhandler implements InvocationHandler {
private Object proxyedObj;//被代理对象
public EasyInvocationhandler(Object proxyedObj){
this.proxyedObj=proxyedObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;//定义方法的返回值对象
System.out.println("-----执行方法之前添加的业务");
//正常执行业务逻辑
result =method.invoke(proxyedObj,args);
System.out.println("执行方法之后的处理");
return result;
}
}
这里的关键点在于Proxy.newProxyInstance方法的第三个参数,即EasyInvocationhandler实例。这个实例实现了InvocationHandler接口,并且重写了invoke方法。
invoke方法的作用
当代理对象的方法被调用时,实际调用的是EasyInvocationhandler类中的invoke方法。这是因为代理对象是通过Proxy.newProxyInstance创建的,它会捕获所有的方法调用,并将它们转发给EasyInvocationhandler的invoke方法。
主函数:
package com.easy.staticproxy.dynamic.jdk;
import com.easy.staticproxy.EasyA;
import com.easy.staticproxy.Shopping;
import java.lang.reflect.Proxy;
public class Factory {
public static Object getProxy(Object obj){
//JDK代理只能实现接口中的方法
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),//类加载器
obj.getClass().getInterfaces(),//实现的接口
new EasyInvocationhandler(obj));
}
public static void main(String[] args) {
EasyObj easy=new EasyObj();
Object obj=getProxy(easy);//动态生成的一个代理类的对象
// System.out.println(obj);
if(obj instanceof EasyInterface){
System.out.println("obj是代理对象是EasyInterface的实例");
EasyInterface e=(EasyInterface) obj;
e.easy();
}
// Class c=obj.getClass();
// System.out.println(c);
EasyA easya=new EasyA();
obj=getProxy(easya);
System.out.println(obj instanceof Shopping);
Shopping s=(Shopping) obj;
s.shopping();
// System.out.println(obj.getClass());
}
}
三、AOP
通过@Aspect来标注当前面是一个切面
我们可以通过@Pointcut注解来定义被拦截的方法,可以设置下面的五种通知方式
下面是示例代码:
package com.easy.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AOPObj {
//定义切点
@Pointcut("execution(* com.easy.controller.EasyAController.testA(..))")
public void pointCuttesta(){}
@Before("pointCuttesta()")
public void before(){
System.out.println("---前置通知");
}
}
我们将切入点设在了EasyController类中调用TestA类的方法时,并且设置成在调用方法前进行通知,我们执行代码并访问页面调用TestA中的方法,控制台输出结果如下:
后两行是test方法中定义的操作。
四、接收前端参数
我们在类的前面用@Requestmapping定义出一级路径。
1.方法的参数名称和前台传递的参数名一样
@RequestMapping("parama")
public String paramA(String name){
return "springmvc接收到的参数是:"+name;
}
我们在前台输入的时候也应该是name
我们在地址上面写name=张三,所以它输出的就是张三
2.同时接受多个参数 Map接收 非常灵活 有安全问题
@RequestMapping("paramb")
public Map paramb(@RequestParam Map params){
return params;
}
@RequestParam Map params: 表示从HTTP请求中读取所有的请求参数,并将它们作为键值对存储在一个Map中。这里的params就是用来接收这些键值对的变量。
我们在地址上填入什么都可以被写入Map,这很不安全。
3.封装对象来接收参数 程序中只接收我们需要的数据
我们定义一个Staff类,里面有id,name,salary这三个属性,我们将Staff类的对象当做参数传入,只有在Staff中定义的属性才能被写入。
Staff类的代码
package com.easy.bean;
public class Staff {
private Integer id;
private String name;
private int salary;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
@RequestMapping("paramc")
public Staff paramc(Staff staff){
return staff;
}
我们没有传入id所以它显示的是null,我们输入age但是Staff中未定义,所以他也无法写入。
4.获取地址上的参数
@RequestMapping("paramd/{id}")
public String paramd(@PathVariable Integer id, HttpServletRequest request){
String username=request.getParameter("username");
return "接收到的参数是:"+id+"----"+username;
}
@PathVariable从 URL 中提取路径变量,并将这些变量用作方法参数。
从地址上获取参数id=12。
作用域
五、转发和重定向
1.转发:同一个服务器中不同的服务进行转发
浏览器发送了一个请求 可以转发到项目中受保护的资源WEB-INF
转发是request对象执行forward方法
@RequestMapping("methoda")
public String methodA(){
System.out.println("---methodA");
return "forward:/methodb";
}
@RequestMapping("methodb")
@ResponseBody
public String methodB(){
System.out.println("---methodB");
return "this is methodB";
}
当我们输入methodA的地址时,它显示的是methodB中的方法,但地址不会改变,说明浏览器只发送了一次请求。
2.重定向:可以在不同的服务之间跳转
浏览器发送了两次请求 重定向是通过response对象通知浏览器重新访问 redirect
@RequestMapping("methoda")
public String methodA(){
System.out.println("---methodA");
// return "forward:/methodb";
// return "redirect:/methodb";//重定向
return "redirect:http://www.baidu.com";
}
@RequestMapping("methodb")
@ResponseBody
public String methodB(){
System.out.println("---methodB");
return "this is methodB";
}
在methodA中输入methodB的地址或者百度的地址,浏览器请求一次,然后methodA将methodB或者百度的地址回复给浏览器,然后浏览器按照地址进行第二次请求。
六、拦截器
定义一个类来继承HandlerInterceptor接口,重写里面的三个方法,使它们分别在方法调用前,调用后、请求处理完毕后打印日志。
拦截器代码
package com.easy.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class EasyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("----preHandle");
// return HandlerInterceptor.super.preHandle(request, response, handler);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("----postHandle");
// HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("----afterCompletion---整个请求处理完毕");
// HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
@RequestMapping("methodc")
@ResponseBody
public String methodC(){
System.out.println("---methodc");
return "index.jsp";
}
执行代码后,输入地址后会显示index.jsp字符串。日志打印结果如下
七、全局控制
首先定义一个Staff类
package com.easy.bean;
public class Staff {
private int id;
private String name;
private String sex;
private int salary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
然后我们通过CommonResult类定义返回结果的格式
package com.easy.common;
public class CommonResult {
private int code;
private String message;
private Object data;
public CommonResult(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public static CommonResult success(int code, String message, Object data) {
return new CommonResult(code, message, data);
}
public static CommonResult success(int code, String message) {
return new CommonResult(code, message, null);
}
public static CommonResult success(Object data) {
return new CommonResult(200, "操作成功", data);
}
public static CommonResult success() {
return new CommonResult(200, "操作成功", null);
}
public static CommonResult fail(int code, String message, Object data) {
return new CommonResult(code, message, data);
}
public static CommonResult fail(int code, String message) {
return new CommonResult(code, message, null);
}
public static CommonResult fail(){
return new CommonResult(400,"操作失败",null);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
下面是增删改查Staff类对象的代码实现,每个都默认会返回成功的结果。
@ExceptionHandler(Exception.class) 注解用于指定一个方法来处理特定类型的异常。在这个例子中,它处理所有类型的Exception异常。当一个异常被抛出时,Spring框架会检查是否有用此注解标记的方法,并调用它来处理异常。
package com.easy.controller;
import com.easy.bean.Staff;
import com.easy.common.CommonResult;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@ControllerAdvice//全局控制
//@RequestMapping("staff")
public class StaffController {
@GetMapping("staff")
public CommonResult getList(Staff staff){
List<Staff> list=null;
System.out.println("获取数据");
return CommonResult.success(list);
}
@PostMapping("staff")
public CommonResult addStaff(Staff staff){
System.out.println("新增数据");
return CommonResult.success();
}
@DeleteMapping("staff/{id}")
public CommonResult deleteStaff(@PathVariable int id){
System.out.println("删除数据"+id);
return CommonResult.success();
}
@PutMapping("staff")
public CommonResult editStaff(Staff staff){
System.out.println("编辑数据");
return CommonResult.success();
}
@RequestMapping("ex")
public CommonResult ex(){
int a=12/0;
return CommonResult.success();
}
@ExceptionHandler(Exception.class)
@ResponseBody
public CommonResult exh(){
return CommonResult.success(200,"11111");
}
}
输入@ControllerAdvice就是全局控制,不写的话就只能控制本类。
八、SpringMVC运行原理
1.用户通过浏览器发起一个 HTTP 请求,该请求会被 DispatcherServlet(前端控制器)拦截;
2.DispatcherServlet 调用 HandlerMapping(处理器映射器)找到具体的处理器(Handler)及拦截器,
3.HandlerMapping将Handler以 HandlerExecutionChain 执行链的形式返回给 DispatcherServlet。
4.DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
5.HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(即 Controller 控制器)对请求进行处理;
6.Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC 的底层对象,包括 Model 数据模型和 View 视图信息);
7.HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
8.DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
9.ViewResolver 解析完成后,会将 View 视图并返回给 DispatcherServlet;
10.DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
11.视图负责将结果显示到浏览器(客户端)。