一、獲取Spring容器對象
1.1 實現(xiàn)BeanFactoryAware接口
實現(xiàn)BeanFactoryAware接口,然后重寫setBeanFactory
方法废菱,就能從該方法中獲取到Spring容器對象。
@Service
public class PersonService implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void add() {
Person person = (Person) beanFactory.getBean("person");
}
}
1.2 實現(xiàn)ApplicationContextAware接口
實現(xiàn)ApplicationContextAware
接口,然后重寫setApplicationContext
方法驶冒,也能從該方法中獲取到Spring容器對象。
@Service
public class PersonService2 implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void add() {
Person person = (Person) applicationContext.getBean("person");
}
}
1.3 實現(xiàn)ApplicationListener接口
實現(xiàn)ApplicationListener
接口韵卤,需要注意的是該接口接收的泛型是ContextRefreshedEvent
類骗污,然后重寫onApplicationEvent
方法,也能從該方法中獲取到Spring容器對象沈条。
@Service
public class PersonService3 implements ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext applicationContext;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
applicationContext = event.getApplicationContext();
}
public void add() {
Person person = (Person) applicationContext.getBean("person");
}
}
二需忿、初始化bean
Spring中支持3種初始化bean的方法:
xml中指定init-method方法
使用@PostConstruct注解
實現(xiàn)InitializingBean接口
第一種方法太古老了,現(xiàn)在用的人不多蜡歹,具體用法就不介紹了屋厘。
2.1 使用@PostConstruct注解
在需要初始化的方法上增加@PostConstruct注解,這樣就有初始化的能力月而。
@Service
public class AService {
@PostConstruct
public void init() {
System.out.println("===初始化===");
}
}
@Component
public class AlipayUtils {
@Resource
private AlipayConfigIOS configIOS;
@Resource
private AlipayConfigAndroid configAndroid;
public AlipayClient alipayClientIOS;
public AlipayClient alipayClientAndroid;
@PostConstruct
public void init(){
System.out.println("===初始化===");
//構(gòu)建IOS
alipayClientIOS = new DefaultAlipayClient(
configIOS.getGateWay(),
configIOS.getAppId(),
configIOS.getAppPrivateKey(),
configIOS.getFormat(),
configIOS.getCharset(),
configIOS.getAliPayPublicKey(),
configIOS.getSignType());
//構(gòu)建Android
alipayClientAndroid = new DefaultAlipayClient(
configAndroid.getGateWay(),
configAndroid.getAppId(),
configAndroid.getAppPrivateKey(),
configAndroid.getFormat(),
configAndroid.getCharset(),
configAndroid.getAliPayPublicKey(),
configAndroid.getSignType());
}
}
2.2 實現(xiàn)InitializingBean接口
實現(xiàn)InitializingBean
接口汗洒,重寫afterPropertiesSet
方法,該方法中可以完成初始化功能父款。
@Service
public class BService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("===初始化===");
}
}
三溢谤、自定義自己的Scope
我們都知道Spring默認支持的Scope只有兩種:
- singleton 單例,每次從spring容器中獲取到的bean都是同一個對象憨攒。
- prototype 多例世杀,每次從spring容器中獲取到的bean都是不同的對象。
Spring web又對Scope進行了擴展肝集,增加了:
- RequestScope 同一次請求從spring容器中獲取到的bean都是同一個對象瞻坝。
- SessionScope 同一個會話從spring容器中獲取到的bean都是同一個對象。
即便如此杏瞻,有些場景還是無法滿足我們的要求所刀。比如,我們想在同一個線程中從spring容器獲取到的bean都是同一個對象伐憾,該怎么辦勉痴?這就需要自定義Scope了。
3.1 第一步實現(xiàn)Scope接口
public class ThreadLocalScope implements Scope {
private static final ThreadLocal THREAD_LOCAL_SCOPE = new ThreadLocal();
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Object value = THREAD_LOCAL_SCOPE.get();
if (value != null) {
return value;
}
Object object = objectFactory.getObject();
THREAD_LOCAL_SCOPE.set(object);
return object;
}
@Override
public Object remove(String name) {
THREAD_LOCAL_SCOPE.remove();
return null;
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
}
3.2 第二步將新定義的Scope注入到Spring容器中
@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope("threadLocalScope", new ThreadLocalScope());
}
}
3.3 第三步使用新定義的Scope
@Scope("threadLocalScope")
@Service
public class CService {
public void add() {
}
}
四树肃、自定義類型轉(zhuǎn)換
Spring目前支持3種類型轉(zhuǎn)換器:
Converter<S,T>:將 S 類型對象轉(zhuǎn)為 T 類型對象
ConverterFactory<S, R>:將 S 類型對象轉(zhuǎn)為 R 類型及子類對象
GenericConverter:它支持多個source和目標類型的轉(zhuǎn)化蒸矛,同時還提供了source和目標類型的上下文,這個上下文能讓你實現(xiàn)基于屬性上的注解或信息來進行類型轉(zhuǎn)換。
這3種類型轉(zhuǎn)換器使用的場景不一樣雏掠,我們以Converter<S,T>為例斩祭。假如:接口中接收參數(shù)的實體對象中,有個字段的類型是Date乡话,但是實際傳參的是字符串類型:2021-01-03 10:20:15摧玫,要如何處理呢?
4.1 第一步绑青,定義一個實體User
@Data
public class User {
private Long id;
private String name;
private Date registerDate;
}
4.2 第二步诬像,實現(xiàn)Converter接口
public class DateConverter implements Converter<String, Date> {
private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
private static final String shortDateFormat = "yyyy-MM-dd";
@Override
public Date convert(String source) {
if(StringUtils.isEmpty(value)) {
return null;
}
value = value.trim();
try {
if(value.contains("-")) {
SimpleDateFormat formatter;
if(value.contains(":")) {
formatter = new SimpleDateFormat(dateFormat);
}else {
formatter = new SimpleDateFormat(shortDateFormat);
}
Date dtDate = formatter.parse(value);
return dtDate;
}else if(value.matches("^\\d+$")) {
Long lDate = new Long(value);
return new Date(lDate);
}
} catch (Exception e) {
throw new RuntimeException(String.format("parser %s to Date fail", value));
}
throw new RuntimeException(String.format("parser %s to Date fail", value));
}
}
4.3 第三步,將新定義的類型轉(zhuǎn)換器注入到Spring容器中
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DateConverter());
}
}
4.4 第四步闸婴,調(diào)用接口
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/save")
public String save(@RequestBody User user) {
return "success";
}
}
請求接口時User對象中registerDate字段會被自動轉(zhuǎn)換成Date類型坏挠。
五、Enable開關(guān)
不知道你有沒有用過Enable開頭的注解邪乍,比如:EnableAsync
降狠、EnableCaching
、EnableAspectJAutoProxy
等庇楞,這類注解就像開關(guān)一樣榜配,只要在@Configuration
定義的配置類上加上這類注解,就能開啟相關(guān)的功能吕晌,讓我們一起實現(xiàn)一個自己的開關(guān)蛋褥。
5.1 第一步,定義一個LogFilter
public class LogFilter 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("記錄請求日志");
chain.doFilter(request, response);
System.out.println("記錄響應(yīng)日志");
}
@Override
public void destroy() {
}
}
5.2 第二步聂使,注冊LogFilter
@ConditionalOnWebApplication
public class LogFilterWebConfig {
@Bean
public LogFilter timeFilter() {
return new LogFilter();
}
}
注意壁拉,這里用了@ConditionalOnWebApplication注解,沒有直接使用@Configuration注解柏靶。
5.3 第三步,定義開關(guān)@EnableLog注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(LogFilterWebConfig.class)
public @interface EnableLog {
}
5.4 第四步溃论,啟動類加上@EnableLog注解
只需在Springboot
啟動類加上@EnableLog
注解即可開啟LogFilter
記錄請求和響應(yīng)日志的功能屎蜓。
轉(zhuǎn)載自:Spring 那些讓你愛不釋手的代碼技巧