首页
统计
留言板
友接
推荐
免费图床
服务监控
Search
1
immich开源相册部署教程
852 阅读
2
GraalVM将Java打包原生Native应用
332 阅读
3
将旧手机改造成Linux服务器
279 阅读
4
Java函数式编程
187 阅读
5
FRP内网穿透教程
169 阅读
编程语言
Java
Python
Go
单片机
Arduino
ESP8266
ESP32
STM32
51单片机
树莓派
运维
Docker容器
随身小记
登录
Search
标签搜索
Spring
SpringMVC
Java
docker
DSM
群晖
iptables
ssh
spring
mybaits
redis
SpringBoot
消息队列
科长
累计撰写
36
篇文章
累计收到
5
条评论
首页
栏目
编程语言
Java
Python
Go
单片机
Arduino
ESP8266
ESP32
STM32
51单片机
树莓派
运维
Docker容器
随身小记
页面
统计
留言板
友接
推荐
免费图床
服务监控
搜索到
36
篇与
科长
的结果
2023-07-04
SpringMVC之DispatcherServlet前置控制器
简介DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自己定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理,是配置spring MVC的第一步。DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。运行流程主要职责DispatcherServlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下:1、文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;2、通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);3、通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);4、通过ViewResolver解析逻辑视图名到具体视图实现;5、本地化解析;6、渲染具体的视图等;7、如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。DispatcherServlet初始化的上下文加载的Bean是只对SpringMVC有效的Bean,如Controller、HandlerMapping、HandlerAdapter等等,该初始化上下文只加载Web相关组件。DispatcherServlet初始化主要做了如下两件事情:1、初始化SpringMVC使用的Web上下文,并且可能指定父容器为(ContextLoaderListener加载了根上下文);2、初始化DispatcherServlet使用的策略,如HandlerMapping、HandlerAdapter等。配置DispatcherServlet下面的例子演示了如何配置DispatcherServlet。<web-app> <servlet> <servlet-name>example</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param>//初始化参数 <param-name>contextConfigLoaction</param-name> <param-value>classpath:applicationcontext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup>//启动级别为1 </servlet> <servlet-mapping> <servlet-name>example</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping> </web-app>在上面的例子里,所有以.form结尾的请求都会由名为example的DispatcherServlet处理,这只是配置Spring Web MVC的第一步
2023年07月04日
112 阅读
0 评论
0 点赞
2023-07-04
SpringMVC框架简介
Spring MVC基本信息Spring 框架提供了构建Web应用程序的全功能MVC模块。使用Spring可插入的MVC架构,可以选择的是使用内置的Spring Web框架还是Struts这样的Web框架。通过策略接口,Spring框架是高度可配置的,而且包含多种视图技术,例如JavaServer Pages(JSP)技术、Velocity、Tiles、iText和POI。Spring MVC框架并不知道使用的视图,所以不会强迫您只使用JSP技术。Spring MVC分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。Spring MVC 简介SpringMVC指的是Spring的模型-视图-控制器(MVC)框架,是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。处理器是你的应用中注解了@Controller和@RequestMapping的类和方法,Spring为处理器方法提供了极其多样的灵活配置。Spring3.0以后提供了@Controller注解机制、@PathVariable注解以及一些其他的特性,你可以使用它们进行RESTful web站点和应用的开发。Spring MVC的优点容易和其它View框架无缝集成,采用IoC便于测试它是体格典型的教科书式的mvc架构,而不像struts等都是变种或者不是完全基于mvc的系统的框架,Spring适用于初学者或者想了解mvc的人它和tapestry一样是一个纯正的servlet系统,这也是它和tapestry相比 struts所没有的优势。而且框架本身有代码,而且看起来也不费劲比较简单可以理解Spring MVC运行原理客户端请求提交到DispatcherServlet由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的ControllerDispatcherServlet将请求提交到ControllerController调用业务逻辑处理后,返回ModelAndViewDispatcherServlet查询一个或多个ViewResouler视图解析器,找到ModelAndView指定的视图视图负责将结果回显到客户端
2023年07月04日
101 阅读
0 评论
0 点赞
2023-07-04
Spring框架功能模块及概念
Spring框架功能模块及概念Spring框架采用分层架构,功能要素被分成可20个模块,分为 Core Contatiner、Data Access/Integration、Web、AOP、Instrumentation、Messaging和Test1、核心容器Core ContainerSpring的核心容器是其他模块建立的基础,它主要由Beans模块、Core模块、Context模块、Context-support模块和SpEl模块组成。Beans模块提供了BeanFactory,BeanFactory是工程模式的实现,被Spring托管的对象被称作为BeanCore核心模块提供了Spring框架的基本组成部分,其包括IoC和DI功能Context上下文管理,建立在Core和Beans模块的基础之上,它是访问定义和配置任何对象的工具,ApplicationContext接口是上下文的焦点Context-support 提供了对第三方依赖嵌入Spring应用的集成支持SpEL模块:是Spring 3.0后新增的模块,它提供了Spring Expression Language支持,是运行时查询和操作对象图的强大的表达式语言2、数据访问数据访问/集成层包括JDBC、ORM、OXM、JMS和Transactions模块JDBC模块提供了一个JDBC的抽象层,大幅度地减少了在开发过程中对数据库操作的编码ORM模块对流行的对象关系映射API,包括JPA、JDO和Hibernate提供了集成层支持OXM模块提供了一个支持对象/ XML映射的抽象层实现,如JAXB、Castor、XMLBeans、JiBX和XStreamJMS 模块指 Java 消息传递服务,包含使用和产生信息的特性,自 4.1 版本后支持与Spring-message模块的集成3、WEB支持Spring的Web层包括WebSocket、Servlet、Web和Portlet模块WebSocket模块Spring 4.0以后新增的模块,它提供了WebSocket 和SockJS的实现,以及对STOMP的支持Servlet模块也称为Spring-webmvc模块,包含了Spring的模型—视图—控制器(MVC)和REST Web Services实现的Web应用程序Web模块提供了基本的Web开发集成特性,例如:多文件上传功能、使用Servlet监听器来初始化IoC容器以及Web应用上下文Portlet模块提供了在Portlet环境中使用MVC实现,类似Servlet模块的功能4、Spring AOP/IoCSpring AOP及IoC详细介绍和使用方法可见Java Spring框架
2023年07月04日
72 阅读
0 评论
0 点赞
iptables之网络地址转换SNAT
2023年07月04日
88 阅读
0 评论
0 点赞
2023-07-04
源地址转换是内网地址向外访问时,发起访问的内网ip地址转换为指定的ip地址(可指定具体的服务以及相应的端口或端口范围),这可以使内网中使用保留ip地址的主机访问外部网络,即内网的多部主机可以通过一个有效的公网ip地址访问外部网络。模拟环境可以访问互联网master:192.168.2.100/10.0.10.4不可以访问互联网node1:10.0.10.2node2:10.0.10.3需求:现在想要node1和node2能够访问需联网根据上图中的网络环境可以得知:内网不能上网的IP段为10.0.10.0/24能够上网的机器拥有双网卡,它的出口IP为192.168.2.100能够上网额机器可以与10.0.10.2/10.0.10.3互相访问实现上网:现在需要10.0.10.0/24这个IP段的所有机器能够使用的特定的IP192.168.2.100实现访问互联网在master机器上安装iptables防火墙# 关闭自带的firewalld systemctl stop firewalld systemctl disable firewalld # 安装iptables yum -y install iptables iptables-services systemctl start iptables systemctl enable iptables在master机器上开启路由转发功能vim /etc/sysctl.conf net.ipv4.ip_forward = 1 # 将此行写入配置文件 sysctl -p # 验证一下清除iptables的所有规则iptables -F iptables -F -t nat iptables -nL -t nat添加新的snat规则iptables -t nat -A POSTROUTING -s 10.0.10.0/24 -j SNAT --to 192.168.2.100这个规则表示:当流量来自10.0.10.*的IP时,则需在出口路由设备上需要配置源地址转换,转换成192.168.2.100如果我们的出口IP192.168.2.100是动态的,我们可以将转发绑定到网卡上iptables -t nat -A POSTROUTING -s 10.0.10.0/24 -o ens33 -j MASQUERADEens33 是master能够访问互联网的网卡,需要改成自己实际的网卡配置node节点上网我们需要分别设置node1:10.0.10.2/node2:10.0.10.3这台机器的网络配置[root@node1 ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens33 TYPE="Ethernet" PROXY_METHOD="none" BROWSER_ONLY="no" BOOTPROTO="static" DEFROUTE="yes" IPV4_FAILURE_FATAL="no" IPV6INIT="yes" IPV6_AUTOCONF="yes" IPV6_DEFROUTE="yes" IPV6_FAILURE_FATAL="no" IPV6_ADDR_GEN_MODE="stable-privacy" NAME="ens33" UUID="90f3297d-0600-4d78-8c98-b7823d4b8e22" DEVICE="ens33" ONBOOT="yes" IPADDR=10.0.10.2 # node1的IP地址 GATEWAY=10.0.10.4 # master的内网IP NETMASK=255.255.255.0 DNS1=114.114.114.114[root@node2 ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens33 TYPE="Ethernet" PROXY_METHOD="none" BROWSER_ONLY="no" BOOTPROTO="static" DEFROUTE="yes" IPV4_FAILURE_FATAL="no" IPV6INIT="yes" IPV6_AUTOCONF="yes" IPV6_DEFROUTE="yes" IPV6_FAILURE_FATAL="no" IPV6_ADDR_GEN_MODE="stable-privacy" NAME="ens33" UUID="90f3397d-0600-6d97-8c18-b7323d4b8e19" DEVICE="ens33" ONBOOT="yes" IPADDR=10.0.10.3 # node1的IP地址 GATEWAY=10.0.10.4 # master的内网IP NETMASK=255.255.255.0 DNS1=114.114.114.114可以通过ping baidu.com 进行网络验证网速优化# vi /etc/sysctl.conf net.ipv4.ip_conntrack_max = 1048576 # vi /etc/modprobe.conf options ip_conntrack hashsize=131072 iptables -I INPUT -p udp --sport 53 -j ACCEPT iptables -I INPUT -p tcp --sport 53 -j ACCEPT导入导出规则#将iptables规则文件保存在 /etc/sysconfig/iptables 中,iptables服务启动时会自动还原规则 iptables-save > /etc/sysconfig/iptables # 停止iptables服务会清空掉所有表的规则 systemctl stop iptables # 启动iptables服务会自动还原/etc/sysconfig/iptables 中的规则 systemctl start iptables
2023-07-03
docker运行群晖系统
闲来无事在gayhub中刷到一个项目virtual-dsm利用docker搭建DSM7 群晖系统docker-compose.ymlversion: "3" services: dsm: container_name: dsm image: kroese/virtual-dsm:latest environment: DISK_SIZE: "16G" #磁盘大小 CPU_CORES: "4" #使用cpu核心数 RAM_SIZE: "2048M" #使用内存大小 devices: #如果设备不支持kvm虚拟化 则删除这个配置 - /dev/kvm - /dev/vhost-net cap_add: - NET_ADMIN ports: - 5000:5000 volumes: - /opt/dsm:/storage restart: on-failure stop_grace_period: 1mdocker命令docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-timeout 60 kroese/virtual-dsm:latest部署的过程中可以看到正在下载DSM的系统等待日志输出You can now login to DSM at port 5000时到浏览器访问服务设置完用户名密码后即可登录正常访问群晖系统
2023年07月03日
108 阅读
0 评论
0 点赞
2023-07-03
Java Spring框架
1、什么是Spring?Spring 是一个支持快速开发的Java EE应用程序框架。他提供了一系列底层容器和基础设施,并可以快速集成大量常用的开源框架。是开发Java EE项目的必备。2、Spring FrameworkSpring Framework 主要包括几个模块:支持 IoC 和 AOP 的容器支持 JDBC 和 ORM 的数据访问模块支持申明式事物模块支持基于 Servlet 的 MVC开发支持基于 Reactive 的 WEB 开发以及集成 JMS、JavaMail、JMX、缓存等其他模块3、IoC 容器3-1、初识IoC、 JavaBeanSpring 提供的容器又叫做 IoC容器。IoC全称Inversion of Control,直译为控制反转。为什么需要IoC?按照以往的开发的方式假设我们需要通过UserService来获取指定用户的信息:public class UserService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config); public User getUserById(Long userId){ try(Connection conn = dataSource.getConnection()){ ... return user; } } }为了从数据库中查询出用户,UserService持有一个DataSource。为了实例化一个HikariDataSource,又需要实例化一个HikariConfig查询完用户,比如需要通过UserRoleService再去查询用户的角色信息:public class UserRoleService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config); public User getUserRoleByUserId(Long userId){ try(Connection conn = dataSource.getConnection()){ ... return role; } } }因为UserRoleService中也需要去访问数据库,所以不得不又实例化了一个HikariDataSource现在我们处理用户发送过来的请求:public class UserInfoServlet extends HttpServlet { private UserService userService = new UserService(); private UserRoleService userRoleService = new UserRoleService(); protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { long userId = getFromCookie(req); User user = userService.getUserById(userId); Role role = userRoleService.getUserRoleByUserId(userId); .... } }上述的每一个Service组件都是以new的形式创建出来的,其有部分缺点:实例化一个组件比较复杂,例如UserService 和 UserRoleService 都需要创建HikariDataSource ,实际上需要读取配置,才能实例化HikariConfig,然后再去实例化HikariDataSource没有必要让每个Service组件分别去创建DataSource的实例,完全可以共享一个DataSource 实例,但是由谁去创建,谁去获取创建好的实例,都不好处理。同理UserInfoServlet或者其他的xxxServlet 需要使用UserService实例或者UserRoleService实例时,也应当共享同一个实例,但是比较复杂不好处理很多组件需要销毁以便释放资源,例如DataSource,但如果组件被对多个组件共享,怎样确保他的使用方已被全部销毁随着组件越来越多,需要共享的组件写起来非常困难,依赖关系也更加复杂从上面几个问题中,不难看出,随着系统越来越庞大,组件越来越多,其组件的生命周期和相互之间的依赖关系如果由组件自身来维护,则会大大增加系统的复杂度,也会导致组件之间耦合度越来越高,测试维护越来越困难因此,核心问题是:谁负责创建组件谁负责根据依赖关系组装组件销毁的时候,如何按照依赖顺序正确销毁解决这些问题的核心方案就是IoC容器在传统的项目中,控制权在程序本身,程序的控制流程完全由开发者控制,例如:UserInfoServlet 创建了UserService, 在创建UserService的时候又创建了DataSource 组件。这种模式的缺点就是,一个组件如果要使用另一个组件,就必须要先知道如何正确的创建它,否则无法正常使用在IoC模式下,控制权发生了反转,即从应用程序转移到了IoC容器,所以组件不再由应用程序自己创建和配置,而是由IoC容器负责。应用程序只需要直接使用已经创建好并且配置好的组件。为了能让组件在IoC容器中被“装配”出来,需要某种“注入”机制。例如UserService自己并不会去创建DataSource,而是等待外部通过setDataSource()方法来注入一个配置好的DataSource:public class UserService { private DataSource dataSource; public void setDataSource(DataSource dataSource){ this.dataSource = dataSource; } }不需要再通过new的形式来实例化一个DataSource组件,而是通过注入的方式注入一个DataSource,这种方式带来了很多好处:UserService不需要关心如何创建的DataSource,因此不需要再去写读取数据库配置之类的代码DataSource 被注入到 UserService 同样也可以被注入到 UserRoleService 中,共享一个组件变得非常简单因此IoC又称为依赖注入,它解决了一个最主要的问题:将组件的创建+配置与组件的使用分离开,并且由IoC容器负责管理组件的生命周期因为IoC容器要负责实例化所有的组件,因此需要告诉容器如何创建组件,以及之间的依赖关系。通过配置XML文件的来实现:<beans> <bean id="dataSource" class="HirkariDataSource" /> <bean id="userService" class="UserService"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="userRoleService" class="UserRoleService"> <property name="dataSource" ref="dataSource" /> </bean> </beans>上述XML配置文件指示IoC容器创建三个JavaBean组件,并把id为dataSource的组件通过属性dataSource(调用setDataSource()函数)注入到了另外两个组件中,在Spring IoC中,我们把所有的组件统称为JavaBean,即配置一个组件就是配置一个JavaBean依赖注入方式从上面可以看出,依赖注入可以通过set()方法实现。同时依赖注入也可以通过构造方法实现通过有参构造注入依赖:public class UserService { private DataSource dataSource; public UserService(DataSource dataSource) { this.dataSource = dataSource; } }无侵入容器 Spring的IoC容器是一个高度可拓展的无侵入容器。所谓无侵入就是指应用程序的组件无需实现Spring的特定接口,或者说组件根本不知道自己在Spring的容器中运行3-2、配置JavaBean的方式项目目录结构spring-ioc-appcontext ├── pom.xml └── src └── main ├── java │ └── com │ └── bystart │ └── learnjava │ ├── Main.java │ └── service │ ├── User.java │ ├── UserService.java │ └── UserRoleService.java └── resources └── application.xml3-2-1、使用XML配置application.xml<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userService" class="com.bystart.learnjava.service.UserService"/> <bean id="userRoleService" class="com.bystart.learnjava.service.UserRoleService" /> <!-- 注入一些属性案例 <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="password" /> <property name="maximumPoolSize" value="10" /> <property name="autoCommit" value="true" /> </bean> --> <!-- 注入一个Bean 案例 <bean id="userService" class="com.bystart.learnjava.service.UserService"> <property name="dataSource" ref="dataSource" /> </bean> --> </beans>Main.java 文件内容:public class Main { public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserService userService = context.getBean(UserService.class); UserRoleService userRoleService = context.getBean(UserRoleService.class); User loginUser = userService.login("username@qq.com","password"); loginUser.setRoleName(userRoleService.getUserRoleNameByUserId(loginUser.getUserId())); } }我们从创建Spring的代码中可以看出,Spring容器就是ApplicationContext,它是一个接口,有很多实现类, 这里我们选择ClassPathXmlApplicationContext表示它会自动从classpath中查找指定的xml配置文件ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");获得了ApplicationContext的实例,就获得了IoC容器的引用。从ApplicationContext中我们可以根据Bean的id获取Bean,但更多的时候一般根据Bean的类型来获取Bean的引用UserService userService = context.getBean(UserService.class);Spring还提供了另一种IoC容器叫BeanFactory,使用方式和ApplicationContext 类似:BeanFactory factory = new XmlBeanFactory(new ClassPathResource("application.xml")); UserService userService = factory.getBean(UserService.class);BeanFactory和ApplicationContext的区别在于,BeanFactory的实现是按需创建,即第一次获取Bean时才创建这个Bean,而ApplicationContext会一次性创建所有的Bean。实际上,ApplicationContext接口是从BeanFactory接口继承而来的,并且,ApplicationContext提供了一些额外的功能,包括国际化支持、事件和通知机制等。通常情况下,我们总是使用ApplicationContext,很少会考虑使用BeanFactory3-2-2、使用注解配置我们可以使用更简单的方式来配置一个JavaBean,那就是使用注解@Component:@Component public class UserRoleService { }@Component 注解就相当于定义了一个Bean,它有一个可选的名称,默认是userRoleService,即小写开头的类名想使用这个Bean也是非常的简单只需要加一个 @Autowired,自动注入到属性,想要注入对应的依赖,则自己也需要被IoC托管,需要加上@Component注解:@Component public class UserService { @Autowired private UserRoleService userRoleService; }@Autowired 还可以加入到构造方法中@Component public class UserService { private UserRoleService userRoleService; public UserService(@Autowired UserRoleService userRoleService){ this.userRoleService = userRoleService; } }开启注解扫描,我们在Main.java添加部分代码:@Configuration @ComponentScan public class Main { public static void main(String[] args){ ApplicationContext context = new AnnotationConfigApplicationContext(Main.class); UserService userService = context.getBean(UserService.class); UserRoleService userRoleService = context.getBean(UserRoleService.class); User loginUser = userService.login("username@qq.com","password"); loginUser.setRoleName(userRoleService.getUserRoleNameByUserId(loginUser.getUserId())); } }Main.class中加入一个注解@Configuration,表示他是一个配置类,因为在创建ApplicationContext时:ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);使用的实现类是AnnotationConfigApplicationContext,他的构造方法必须传入一个标注了@Configuration 的类,此外Main.java还标注了@ComponentScan它表示告诉容器,自动搜索扫描当前类所在的包及子包下所有标注了 @Component的类给创建到IoC容器中进行托管,并根据@Autowired 自动装配使用Annotation配合自动扫描能大幅简化Spring的配置,我们只需要保证:每个Bean被标注为@Component并正确使用@Autowired注入配置类被标注为@Configuration和@ComponentScan所有Bean均在指定包以及子包内4、Spring AOP在AOP编程中,我们经常会遇到下面的概念:Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行Pointcut:切入点,即一组连接点的集合Advice:增强,指特定连接点上执行的动作Introduction:引介,指为一个已有的Java对象动态地增加新的接口Weaving:织入,指将切面整合到程序的执行流程中Interceptor:拦截器,是一种实现增强的方式Target Object:目标对象,即真正执行业务的核心逻辑对象AOP Proxy:AOP代理,是客户端持有的增强后的对象引用4-1、装配AOP我们以UserService 和 UserRoleService 为例,我们给UserService的每个业务的方法执行前添加日志,给UserRoleService每个方法执行前后添加日志记录我们定义一个LoggingAspect:@Aspect @Component public class LoggingAspect { // 在 UserService 的每个方法执行前执行该部分代码 @Before("execution(public * com.bystart.learnjava.service.UserService.*(..))") public void doAddUserServiceLog(){ System.out.println("开始添加日志"); ... } // 在 UserRoleService 的每个方法 执行前/后 执行该部分代码 @Around("execution(public * com.bystart.learnjava.service.UserRoleService.*(..))") public Object doAddUserRoleServiceLog(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("开始添加执行前日志"); // 获取目标方法的返回内容 Object retVal = pjp.proceed(); System.err.println("开始添加执行后的日志,目标方法返回内容:" + pjp.getSignature()); // 真正返回内容给调用方 return retVal; } }观察doAddUserServiceLog()方法,我们定义了一个@Before注解,后面的字符串是告诉AspectJ应该在何处执行该方法,这里写的意思是:执行UserService的每个public方法前执行doAddUserServiceLog()代码再观察doAddUserRoleServiceLog()方法,我们定义了一个@Around注解,它和@Before不同,@Around可以决定是否执行目标方法,因此,我们在doAddUserRoleServiceLog()内部先打印日志,再调用方法,最后打印日志后返回结果在LoggingAspect类的声明处,除了用@Component表示它本身也是一个Bean外,我们再加上@Aspect注解,表示它的@Before标注的方法需要注入到UserService的每个public方法执行前,@Around标注的方法需要注入到UserRoleService的每个public方法执行前后紧接着,我们需要给@Configuration类加上一个@EnableAspectJAutoProxy注解:@Configuration @ComponentScan @EnableAspectJAutoProxy public class Main { public static void main(String[] args){ ApplicationContext context = new AnnotationConfigApplicationContext(Main.class); UserService userService = context.getBean(UserService.class); UserRoleService userRoleService = context.getBean(UserRoleService.class); User loginUser = userService.login("username@qq.com","password"); loginUser.setRoleName(userRoleService.getUserRoleNameByUserId(loginUser.getUserId())); } }Spring的IoC容器看到这个注解,就会自动查找带有@Aspect的Bean,然后根据每个方法的@Before、@Around等注解把AOP注入到特定的Bean中那么LoggingAspect是怎么实现将方法注入到其他Bean,并且在目标方法执行前后触发LoggingAspect的方法的呢其实原理非常的简单,它是创建了一个代理对象:public class UserServiceAopProxy extends UserService { private UserService target; private LoggingAspect aspect; public UserServiceAopProxy(UserService target, LoggingAspect aspect){ this.target = target; this.aspect = aspect; } public User login(String email, String password){ // 先执行Aspect的代码 aspect.doAddUserServiceLog(); // 再执行原有的逻辑 return target.login(email, password); } }这些都是Spring容器启动时为我们自动创建的注入了Aspect的子类,它取代了原始的UserService(原始的UserService实例作为内部变量隐藏在UserServiceAopProxy中)。如果我们打印从Spring容器获取的UserService实例类型,它类似UserService$$EnhancerBySpringCGLIB$$1f44e01c,实际上是Spring使用CGLIB动态创建的子类,但对于调用方来说,感觉不到任何区别。Spring对接口类型使用JDK动态代理,对普通类使用CGLIB创建子类。如果一个Bean的class是final,Spring将无法为其创建子类。可见,虽然Spring容器内部实现AOP的逻辑比较复杂(需要使用AspectJ解析注解,并通过CGLIB实现代理类),但我们使用AOP非常简单,一共需要三步:定义执行方法,并在方法上通过AspectJ的注解告诉Spring应该在何处调用此方法标记@Component和@Aspect在@Configuration类上标注@EnableAspectJAutoProxy4-2、拦截器类型顾名思义,拦截器有以下类型:@Before:这种拦截器先执行拦截代码,再执行目标代码。如果拦截器抛异常,那么目标代码就不执行了@After:这种拦截器先执行目标代码,再执行拦截器代码。无论目标代码是否抛异常,拦截器代码都会执行@AfterReturning:和@After不同的是,只有当目标代码正常返回时,才执行拦截器代码@AfterThrowing:和@After不同的是,只有当目标代码抛出了异常时,才执行拦截器代码@Around:能完全控制目标代码是否执行,并可以在执行前后、抛异常后执行任意拦截代码,可以说是包含了上面所有功能
2023年07月03日
83 阅读
0 评论
2 点赞
1
...
3
4