Spring 源码分析解答附录
IOC
ioc 是控制反转,俗称依赖注入,是 Spring Framework 的核心功能, 由 BeanFactory, BeanDefinition, BeanDefinitionMap, Spring 缓存池组成。
说说 aop 和 ioc 关系,可以独立实现没有关系, 但 Spring 在实现 ioc 的时候使用到了 aop 的功能。
bean 生命周期
AOP
aop 是面向切面编程,使用切点和增强的概念非侵入式切入代码。aop 主要是实现有静态编译, Java 运行时动态代理,cglib 在运行时动态生成字节码技术
- aop 是什么,有哪些实现方式?
- aop 里面的 cglib 原理是什么? asm 字节码生成技术
- aop 切方法的方法的时候,哪些方法是切不了的?为什么?
- 同类调用为什么无法切?怎么样解决(AOPContext)?
Spring 默认使用 JDK 代理,同类中方法间互相调用是切不了的,因为调用的是 target 的方法, 所以无法切, 可以使用 AOPContext.getProxy() 获得代理对象
启用 AOP
在 conf 类中加注解 @EnableAspectJAutoProxy, 参见 aop-aspectj-support
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy public class AppConfig { }
@Aspect 定义切面
使用 @Aspect 定义一个切面
package top.waterlaw.test; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect public class MyAspectJ { @Pointcut("@annotation(top.waterlaw.app.MyApp)") public void pointCut() { } // @After("pointCut()") // public void advice() { // // advice 切入的时机 // System.out.println("AOP before"); // } // // @Around("pointCut()") // public void around(JoinPoint pjp) { // System.out.println("AOP before"); // } @Around("pointCut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("aOP before"); pjp.getArgs(); // 获得参数 Object retVal = pjp.proceed(); // 执行目标函数 System.out.println("aop after"); return retVal; } }
@Pointcut 定义切点
@Pointcut 定义切点, 生命一个增强 @After,@Around
@Before, @After,@Around 定义增强
@Around 的 JoinPoint 和 ProceedingJoinPoint 的区别是 ProceedingJoinPoint 可以获得函数参数、方法
Bean
bean 作用域有哪些,说一下各种使用场景?
bean 的各种作用域是怎么样实现的?
工具类中如何注入 bean?具体使用场景?
1.使用 MethodInvokingFactoryBean 的静态方法注入
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="i18n/messages" /> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="plm.common.utils.CheckUtil.setResources" /> <!-- 这里配置参数 --> <property name="arguments" ref="messageSource"> </property> </bean>
使用场景则是在国际化的时候,注入一个 messageSource,还有一种不太雅观不推荐的方法,也是网上博客写的方法, 只有在工具类注册为 bean 的时候生效
2.工具类声明为 bean + @PostConstruct
@Component public class MyUtils { @Autowired private IBeanService beanService; private static MyUtils myUtils; @PostConstruct public void init() { myUtils = this; myUtils.beanService = this.beanService; } private MyUtils() { } private static IBeanService getBeanService() { return myUtils.beanService; } public static void util1() { // 该工具方法中可以通过 getBeanService 获得 spring 中注入的实例进行操作了 } }
3.SpringContextUtil.getBean("beanName")
Spring 提供的工具类 SpringContextUtil 也可以拿到 bean
注入的 bean 存在多份的时候有哪些解决办法? 当通过 autowiredByType 进行注入时 ,@Primary > @Priority > beanName
有没有用过 BeanFactory?场景?
ApplicationContext
is a sub-interface of BeanFactory
.
IOC 中的 ApplicationContext 是一个实现 BeanFactory 接口的子类。
注解
spring 支持自带注解和 jsr-250 规范注解,以及 jsr-330 注解
spring-core:
org.springframework.core.annotation.AliasFor
org.springframework.core.annotation.Order
spring-beans:
org.springframework.beans.factory.annotation.Autowired
org.springframework.beans.factory.annotation.Configurable
org.springframework.beans.factory.annotation.Lookup
org.springframework.beans.factory.annotation.Qualifier
org.springframework.beans.factory.annotation.Required
org.springframework.beans.factory.annotation.Value
spring-aspects:
org.springframework.context.annotation.aspectj.EnableSpringConfigured
spring-jms:
org.springframework.jms.annotation.EnableJms
org.springframework.jms.annotation.JmsListener
org.springframework.jms.annotation.JmsListeners
spring-tx:
org.springframework.transaction.annotation.EnableTransactionManagement
org.springframework.transaction.annotation.Transactional
org.springframework.web.servlet.config.annotation.EnableWebMvc
spring-context:
cache 相关
org.springframework.cache.annotation.Cacheable
org.springframework.cache.annotation.CacheConfig
org.springframework.cache.annotation.CacheEvict
org.springframework.cache.annotation.CachePut
org.springframework.cache.annotation.Caching
org.springframework.cache.annotation.EnableCaching
Bean 相关
org.springframework.context.annotation.Bean
org.springframework.context.annotation.ComponentScan
org.springframework.context.annotation.ComponentScans
org.springframework.context.annotation.Conditional
org.springframework.context.annotation.Configuration
org.springframework.context.annotation.DependsOn
org.springframework.context.annotation.Description
org.springframework.context.annotation.EnableAspectJAutoProxy
org.springframework.context.annotation.EnableLoadTimeWeaving
org.springframework.context.annotation.EnableMBeanExport
org.springframework.context.annotation.Import
org.springframework.context.annotation.ImportResource
org.springframework.context.annotation.Lazy
org.springframework.context.annotation.Primary
org.springframework.context.annotation.Profile
org.springframework.context.annotation.PropertySource
org.springframework.context.annotation.PropertySources
org.springframework.context.annotation.Role
org.springframework.context.annotation.Scope
jmx 相关
org.springframework.jmx.export.annotation.ManagedAttribute
org.springframework.jmx.export.annotation.ManagedMetric
org.springframework.jmx.export.annotation.ManagedNotification
org.springframework.jmx.export.annotation.ManagedNotifications
org.springframework.jmx.export.annotation.ManagedOperation
org.springframework.jmx.export.annotation.ManagedOperationParameter
org.springframework.jmx.export.annotation.ManagedOperationParameters
org.springframework.jmx.export.annotation.ManagedResource
scheduling 相关
org.springframework.scheduling.annotation.Async
org.springframework.scheduling.annotation.EnableAsync
org.springframework.scheduling.annotation.EnableScheduling
org.springframework.scheduling.annotation.Scheduled
org.springframework.scheduling.annotation.Schedules
声明 Bean 的注解
org.springframework.stereotype.Component
org.springframework.stereotype.Controller
org.springframework.stereotype.Indexed
org.springframework.stereotype.Repository
org.springframework.stereotype.Service
validation 相关
org.springframework.validation.annotation.Validated
spring-web:
Mapping
org.springframework.web.bind.annotation.Mapping
org.springframework.web.bind.annotation.RequestMapping
org.springframework.web.bind.annotation.DeleteMapping
org.springframework.web.bind.annotation.GetMapping
org.springframework.web.bind.annotation.PatchMapping
org.springframework.web.bind.annotation.PostMapping
org.springframework.web.bind.annotation.PutMapping
请求相关
org.springframework.web.bind.annotation.ControllerAdvice
org.springframework.web.bind.annotation.CookieValue
org.springframework.web.bind.annotation.CrossOrigin
org.springframework.web.bind.annotation.ExceptionHandler
org.springframework.web.bind.annotation.InitBinder
org.springframework.web.bind.annotation.MatrixVariable
org.springframework.web.bind.annotation.ModelAttribute
org.springframework.web.bind.annotation.PathVariable
org.springframework.web.bind.annotation.RequestAttribute
org.springframework.web.bind.annotation.RequestBody
org.springframework.web.bind.annotation.RequestHeader
org.springframework.web.bind.annotation.RequestParam
org.springframework.web.bind.annotation.RequestPart
org.springframework.web.bind.annotation.ResponseBody
org.springframework.web.bind.annotation.ResponseStatus
org.springframework.web.bind.annotation.RestController
org.springframework.web.bind.annotation.RestControllerAdvice
org.springframework.web.bind.annotation.SessionAttribute
org.springframework.web.bind.annotation.SessionAttributes
org.springframework.web.context.annotation.ApplicationScope
org.springframework.web.context.annotation.RequestScope
org.springframework.web.context.annotation.SessionScope
spring-websocket:
org.springframework.web.socket.config.annotation.EnableWebSocket
org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
jsr-250 注解
javax.annotation.Resource
可以根据名字或类名注入 Bean
javax.annotation.PostConstruct
javax.annotation.PreDestroy
jsr-330 注解
javax.inject.Inject
javax.inject.Named / javax.inject.ManagedBean
javax.inject.Singleton
javax.inject.Qualifier / javax.inject.Named
需要引入额外包, 更多请参阅官方文档
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
Spring MVC
项目结构
-
java
-
resources
-
webapp
- WEB-INF
- xxx-servlet.xml
- web.xml
- index.jsp
主要是 web.xml 和 servlet.xml 配置
web.xml 包括配置 spring 核心监听器 , 过滤器,DispatcherServlet
Spring 核心监听器
Spring 核心监听器默认会以 /WEB-INF/applicationContext.xml作为配置文件,
public class ContextLoaderListener extends ContextLoader implements ServletContextListener
ContextLoader 可以指定在 Web 应用程序启动时载入 Ioc 容器, ServletContextListener 监听 ServletContext,
ContextLoaderListener 启动 Web 容器时,读取在 contextConfigLocation 中定义的 xml 文件,自动装配ApplicationContext 的配置信息,并产生 WebApplicationContext 对象,然后将这个对象放置在 ServletContext的属性里。
IOC
数据源,sessionFactory,component-scan,配置事务管理器, AOP 配置提供事务增强, 配置事务的传播特性。
DispatcherServlet
component-scan, 启动Spring MVC的注解功能,完成请求和注解POJO的映射 AnnotationMethodHandlerAdapter, 静态资源访问, 视图解析器, 上传文件,国际化文件和异常处理。
过滤器
容器编码、登录认证、静态资源拦截, 在 DispatcherServlet 前后进行处理
拦截器
拦截器是什么,什么场景使用?
AOP 实现事务管理、日志打印、时间耗时统计、权限认证
说说 DispatcherServlet 做了什么
实现拦截器的方法:
Spring MVC 提供了 Interceptor 拦截机制,用于请求的预处理和后处理。在 Spring MVC 中定义一个拦截器有两种方法:
第一种是实现 HandlerInterceptor 接口,或者继承实现了 HandlerInterceptor 接口的类例如(HandlerInterceptorAdapter);
第二种方法是实现 Spring 的 WebRequestInterceptor 接口(该接口是针对请求的拦截器接口,接口方法参数中没有 response),或者继承实现了 WebrequestInterceptor 的类。两种方式都是在 Handler 的执行周期内进行拦截操作。
如果要实现 HandlerInterceptor 接口,需要实现三个方法,preHandle、postHandle、afterCompletion
preHandle 方法在执行 Handler 方法之前执行,返回 false 表示拦截请求,不在执行后续逻辑,可以用来做权限,日志等。
postHandle 方法在执行 Handler 方法之后,返回 modelAndView 之前执行,由于该方法会在 DispatcherServlet进行返回视图渲染之前被调用,所以此方法多被用于同一处理返回视图,例如将公用的模型数据添加到视图,或者根据其他情况制定公用的视图。
afterCompletion 方法在执行完 Handler 之后执行,由于是在 Controller 方法执行完毕后执行该方法,所以该方法适合进行统一的异常或者日志处理操作。
实现 HandlerInterceptor 接口之后需要在 Spring 的类加载配置文件中配置拦截器实现类,才能使拦截器起到拦截的效果。HandlerInterceptor 类加载配置有两种方式,分别是”针对 HandlerMapping 配置”和 全局配置。
针对 HandlerMapping 配置需要在某个处理器映射器配置中将拦截器作为参数配置进去,之后通过此处理器映射器的 handler 就会使用配置好的拦截器,配置如下:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="hInterceptor1" /> <ref bean="hInterceptor2" /> </list> </property> <property name="order" value="1"></property> </bean> <bean id="hInterceptor1" class="com.sl.interceptors.TestInterceptor"></bean> <bean id="hInterceptor2" class="com.sl.interceptors.TestOrderInterceptor"></bean>
全局配置,springmvc 框架将配置的全局拦截器注入到每个 HandlerMapping 中
<!-- 配置自定义的拦截器 --> <mvc:interceptors> <bean class="com.sl.interceptors.TestInterceptor"></bean> </mvc:interceptors>
实现一个拦截器:
@Component public class TestInterceptor implements HandlerInterceptor { /** * 当目标方法执行之前,执行此方法,返回 false,则不再执行后续逻辑 postHandle、afterCompletion */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("First preHandle 最先执行.."); return true; } /** * 执行目标方法之后,渲染视图之前调。 在转向 jsp 页面之前, 可以对请求域中的属性,或者视图进行修改 */ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("First postHandle 执行目标方法之后,渲染视图之前调。 在转向jsp页面之前,"); } /** * 在渲染视图之后被调用,可以进行日志处理 */ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("First afterCompletion 渲染视图之后调用"); } }
拦截器的指定范围:配置拦截器时可以根据需要制定拦截器作用范围,针对特定处理器或方法进行拦截
<!-- 配置拦截器 --> <!-- 使用 bean 定义一个 Interceptor,直接定义在 mvc:interceptors 根节点下则拦截所有的请求 --> <!-- 定义在 mvc:interceptor 下面的表示是对特定的请求才进行拦截的 --> <mvc:interceptors> <mvc:interceptor> <!-- 指定拦截器作用路径 --> <mvc:mapping path="/product/*" /> <bean class="com.sl.interceptors.TestInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/order/*" /> <bean class="com.sl.interceptors.TestOrderInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
日志框架
Java 日志统一接口
jcl: Jakarta Commons-logging 是 apache 最早提供的日志的门面接口, 也是 spring 默认使用的日志框架。
Commons-logging 的目的是为 “所有的 Java 日志实现” 提供一个统一的接口,它自身的日志功能平常弱(只有一个简单的SimpleLog?),所以一般不会单独使用它。Log4j 的功能非常全面强大,是目前的首选。
slf4j:Simple Logging Facade for Java,为 Java 提供的简单日志 Facade。Facade 门面,更底层一点说就是接口。它允许用户以自己的喜好,在工程中通过 slf4j 接入不同的日志系统。
因此 slf4j 入口就是众多接口的集合,它不负责具体的日志实现,只在编译时负责寻找合适的日志系统进行绑定。具体有哪些接口,全部都定义在 slf4j-api 中。
NDC 和 MDC 功能
spring 自带的 logback 和 log4j 都有 NDC 和 MDC 功能,
log4j 有个叫 MDC(Mapped Diagnostic Context) 的类(技术上使用了 ThreadLocal 实现,重点技术)。具体使用方法请自行查询。
UserFilter
/** * 用户信息相关的filter * * @author 晓风轻 https://xwjie.github.io/PLMCodeTemplate/ * */ public class UserFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 得到用户个人相关的信息(登陆的用户,用户的语言) fillUserInfo((HttpServletRequest) request); try { chain.doFilter(request, response); } finally { // 由于tomcat线程重用,记得清空 clearAllUserInfo(); } } private void clearAllUserInfo() { UserUtil.clearAllUserInfo(); } private void fillUserInfo(HttpServletRequest request) { // 用户信息 String user = getUserFromSession(request); if (user != null) { UserUtil.setUser(user); } // 语言信息 String locale = getLocaleFromCookies(request); // 放入到threadlocal,同一个线程任何地方都可以拿出来 if (locale != null) { UserUtil.setLocale(locale); } } private String getLocaleFromCookies(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); if (cookies == null) { return null; } for (int i = 0; i < cookies.length; i++) { if (UserUtil.KEY_LANG.equals(cookies[i].getName())) { return cookies[i].getValue(); } } return null; } private String getUserFromSession(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return null; } // 从session中获取用户信息放到工具类中 return (String) session.getAttribute(UserUtil.KEY_USER); } @Override public void destroy() { } }
用户信息放入MDC:
/** * 用户工具类 * * @author 晓风轻 https://xwjie.github.io/PLMCodeTemplate/ * */ public class UserUtil { private final static ThreadLocal<String> tlUser = new ThreadLocal<String>(); private final static ThreadLocal<Locale> tlLocale = new ThreadLocal<Locale>() { protected Locale initialValue() { // 语言的默认值 return Locale.CHINESE; }; }; public static final String KEY_LANG = "lang"; public static final String KEY_USER = "user"; public static void setUser(String userid) { tlUser.set(userid); // 把用户信息放到log4j MDC.put(KEY_USER, userid); } /** * 如果没有登录,返回null * * @return */ public static String getUserIfLogin() { return tlUser.get(); } /** * 如果没有登录会抛出异常 * * @return */ public static String getUser() { String user = tlUser.get(); if (user == null) { throw new UnloginException(); } return user; } public static void setLocale(String locale) { setLocale(new Locale(locale)); } public static void setLocale(Locale locale) { tlLocale.set(locale); } public static Locale getLocale() { return tlLocale.get(); } public static void clearAllUserInfo() { tlUser.remove(); tlLocale.remove(); MDC.remove(KEY_USER); } }
log4j 配置
<!-- Appenders --> <appender name="console" class="org.apache.log4j.ConsoleAppender"> <param name="Target" value="System.out" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="[%t]%-d{MM-dd HH:mm:ss,SSS} %-5p:%X{user} - %c - %m%n" /> </layout> </appender>
异常处理
你之前项目中异常/国际化如何处理?
首先看看 Spring MVC 处理异常的 3 中方式,进行比较,最终选用一个比较合适的方式。
-
Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver;
-
Spring MVC 异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器;
-
@ExceptionHandler 注解实现异常处理;
@ControllerAdvice + @ ExceptionHandler
Spring MVC 中通过使用 @ControllerAdvice + @ ExceptionHandler 两个注解可以实现全局的异常捕捉。
@ExceptionHandler 注解的作用是当出现其定义的异常时进行处理的方法,其可以使用 springmvc 提供的数据绑定,比如注入 HttpServletRequest 等,还可以接受一个当前抛出的 Throwable 对象
@ControllerAdvice 注解可以把异常处理器应用到所有控制器 @Controller ,而不是 @Controller 注解的单个控制器,该异常处理器对当前控制器的所有方法有效;如果单独某个控制器需要自定义处理异常,不用顶层的异常处理器,可以在当前控制器内用 @ExceptionHandler 注解 ,这样当前控制器的异常处理就在当前类中。
备注:使用 ControllerAdvice 注解类里面的异常的处理的优先级低于直接定义在处理方法的类中
实现一个异常处理器:
@ControllerAdvice public class ExceptionHandlers { @ExceptionHandler({ArithmeticException.class}) public ModelAndView toException(Exception e){ ModelAndView mv = new ModelAndView("home"); System.out.println("gobal handler exception"); //虽然不能使用 Map 往 request 中存值,但是可以使用下面的方法 mv.addObject("error", e); System.out.println(e); return mv; } }
@Controller @RequestMapping("exception") public class ExceptionController { // 示例1 @RequestMapping("test") public ModelAndView test() { System.out.println(10/0); //抛异常 return new ModelAndView("order/info", "message", "test exception"); } }
比 @ControllerAdvice + @ ExceptionHandler 更好的办法
@ControllerAdvice + @ ExceptionHandler 确实能更好地处理异常, 但是不能打印日志,统计每一个 controller方法的耗时,所以自定义 AOP 来了, 虽然 @ControllerAdvice + @ ExceptionHandler 也是 AOP.
统一返回一个 ResultBean
如果是后端只是设计接口的话, 可以定义一个 ResultBean
@Data // lombok 语法 public class ResultBean<T> implements Serializable { private static final long serialVersionUID = 1L; public static final int NO_LOGIN = -1; public static final int SUCCESS = 0; public static final int FAIL = 1; public static final int NO_PERMISSION = 2; private String msg = "success"; private int code = SUCCESS; private T data; public ResultBean() { super(); } public ResultBean(T data) { super(); this.data = data; } public ResultBean(Throwable e) { super(); this.msg = e.toString(); this.code = FAIL; } }
异常要区分已知异常和未知异常
/** * 处理和包装异常 */ public class ControllerAOP { private static final Logger logger = LoggerFactory.getLogger(ControllerAOP.class); public Object handlerControllerMethod(ProceedingJoinPoint pjp) { long startTime = System.currentTimeMillis(); ResultBean<?> result; try { result = (ResultBean<?>) pjp.proceed(); logger.info(pjp.getSignature() + "use time:" + (System.currentTimeMillis() - startTime)); } catch (Throwable e) { result = handlerException(pjp, e); } return result; } /** * 封装异常信息,注意区分已知异常(自己抛出的)和未知异常 */ private ResultBean<?> handlerException(ProceedingJoinPoint pjp, Throwable e) { ResultBean<?> result = new ResultBean(); // 已知异常 if (e instanceof CheckException) { result.setMsg(e.getLocalizedMessage()); result.setCode(ResultBean.FAIL); } else if (e instanceof UnloginException) { result.setMsg("Unlogin"); result.setCode(ResultBean.NO_LOGIN); } else { logger.error(pjp.getSignature() + " error ", e); //TODO 未知的异常,应该格外注意,可以发送邮件通知等 result.setMsg(e.toString()); result.setCode(ResultBean.FAIL); } return result; } }
AOP 配置
<!-- aop --> <aop:aspectj-autoproxy /> <beans:bean id="controllerAop" class="xxx.common.aop.ControllerAOP" /> <aop:config> <aop:aspect id="myAop" ref="controllerAop"> <aop:pointcut id="target" expression="execution(public xxx.common.beans.ResultBean *(..))" /> <aop:around method="handlerControllerMethod" pointcut-ref="target" /> </aop:aspect> </aop:config>
简单的 controller
/** * 配置对象处理器 * * @author 晓风轻 https://github.com/xwjie/PLMCodeTemplate */ @RequestMapping("/config") @RestController public class ConfigController { private final ConfigService configService; public ConfigController(ConfigService configService) { this.configService = configService; } @GetMapping("/all") public ResultBean<Collection<Config>> getAll() { return new ResultBean<Collection<Config>>(configService.getAll()); } /** * 新增数据, 返回新对象的id * * @param config * @return */ @PostMapping("/add") public ResultBean<Long> add(Config config) { return new ResultBean<Long>(configService.add(config)); } /** * 根据id删除对象 * * @param id * @return */ @PostMapping("/delete") public ResultBean<Boolean> delete(long id) { return new ResultBean<Boolean>(configService.delete(id)); } @PostMapping("/update") public ResultBean<Boolean> update(Config config) { configService.update(config); return new ResultBean<Boolean>(true); } }
国际化
你之前项目中异常/国际化如何处理?
使用 ResourceBundleMessageSource, 在 i18n/messages 加上对应翻译文件,根据请求头 lang 加载翻译文件
工具类注入 bean
工具类里面使用 spring 的 bean,使用了 MethodInvokingFactoryBean 的静态方法注入
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="i18n/messages" /> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="plm.common.utils.CheckUtil.setResources" /> <!-- 这里配置参数 --> <property name="arguments" ref="messageSource"> </property> </bean>
校验工具类 CheckUtil
package plm.common.utils; import org.springframework.context.MessageSource; import plm.common.exceptions.CheckException; public class CheckUtil { private static MessageSource resources; public static void setResources(MessageSource resources) { CheckUtil.resources = resources; } public static void check(boolean condition, String msgKey, Object... args) { if (!condition) { fail(msgKey, args); } } public static void notEmpty(String str, String msgKey, Object... args) { if (str == null || str.isEmpty()) { fail(msgKey, args); } } public static void notNull(Object obj, String msgKey, Object... args) { if (obj == null) { fail(msgKey, args); } } private static void fail(String msgKey, Object... args) { throw new CheckException(resources.getMessage(msgKey, args, UserUtil.getLocale())); } }
更多请查看 https://xwjie.github.io/rule/i18n-valid.html
常用注解
常见的使用多的注解问几个(requestbody, responsebody, ModelAttribute 等)
@ModelAttribute 注释的方法会在此 controller 每个方法执行前被执行,
应用
返回视图和 json 对象
ModelAndView, @ResponseBody: 获取 reponse.getWriter(), redirect 传参数放在 ModelAndView
Model 和 ModelAndView 可以在 redirect/forword 中设置参数
主要扩展点
spring 的主要扩展点有哪些?(最重要最有用的应该就是 bpp 了)
SpringBoot2 | Spring 核心扩展接口 | 核心扩展方法总结(十一)
发表评论
评论列表, 共 0 条评论