11月23
用 security加redis写项目时一直报这个错误,说是没有set方法?我用的data注解怎么可能

这个bug找了一下午,终于找到原因了:redis序列化会查询所有以get和set开头的方法,而我的user继承了security的UserDetails,多了一个集合类型的getAuthorities方法,所有导致无法序列化,使用的是jackson的序列化器

解决办法:
加上@JsonIgnore注解设置不序列化

@JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        ArrayList<NdpAuthority> grantedAuthorities = CollectionUtil.newArrayList();
        if (ObjUtil.isNotEmpty(roles)) {
            roles.forEach(dict -> {
                String roleName = dict.getStr(CommonConstant.NAME);
                NdpAuthority ndpAuthority = new NdpAuthority(roleName);
                grantedAuthorities.add(ndpAuthority);
            });
        }
        return grantedAuthorities;
    }

加个属性,写个对应的set方法
11月23
在实际项目开发过程中,我们有时候需要让项目在启动时执行特定方法。如要实现这些功能:
提前加载相应的数据到缓存中;
检查当前项目运行环境;
检查程序授权信息,若未授权则不能使用后续功能;
执行某个特定方法;

一直想整理一下Springboot启动后自执行某段代码或者方法相关的点,囿于时间问题及担心理解不是太全面所以没整理,后来一想先整理出来,将这一方面的东西记录下来。其实这篇文章不应该体现spring boot,因为自动执行方式不仅限于spring boot,java spring细化到bean的初始化都可以完成。

一、java自身的启动时加载方式

1、static代码块
static静态代码块,在类加载的时候即自动执行。 由于静态代码块没有名字,我们并不能主动调用,它会在类加载的时候,自动执行。所以静态代码块,可以更早的给类中的静态属性,进行初始化赋值操作。并且,静态代码块只会自动被执行一次,因为JVM在一次运行中,对一个类只会加载一次!

2、构造函数constructor
在对象初始化时执行。执行顺序在static静态代码块之后。

3、@PostConstruct注解
@PostConstruct
public void testInit() {
    System.out.printin("postConstruct“);
}
@PostConstruct注解使用在方法上,这个方法在对象依赖注入初始化之后执行。执行节点在BeanPostProcessor的postProcessBeforeInitialization之后,在postProcessAfterInitialization之前。
很多人以为该注解是Spring提供的。其实是Java自己的注解。Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
注意:加了postconstruct注解的方法,如果执行失败,整个程序会无法正常启动!这个方法执行不完,整个程序也启动不了!也不建议将耗时逻辑放到这里面来。

二、Servlet加载的方式
1、实现ServletContextListener接口contextInitialized方法
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

@Slf4j
@Component
public class ServletContextListenerImpl implements ServletContextListener {
    /**
     * 在初始化Web应用程序中的任何过滤器或Servlet之前,将通知所有ServletContextListener上下文初始化。
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        log.info("启动时自动执行 ServletContextListener 的 contextInitialized 方法");
    }
}
注意:该方法会在填充完普通Bean的属性,但是还没有进行Bean的初始化之前执行

三、Spring启动时加载方式
1、实现ServletContextAware接口setServletContext 方法
@Component
public class StartInitServletContext implements ServletContextAware {

    @Override
    public void setServletContext(ServletContext servletContext) {
        System.out.println("StartInitServletContext: 开始处理事情");
    }
}

2、ApplicationListener监听器
创建Listener的方式有两种:
方式1:编程实现ApplicationListener接口
方式2:使用@EventListener注解
注意监听的事件,通常是ApplicationStartedEvent 或者ApplicationReadyEvent,其他的事件可能无法注入bean。

编程实现ApplicationListener接口
/**
* 监听的是 ApplicationStartedEvent 事件,
* 则 ApplicationListener 一定会在 CommandLineRunner 和 ApplicationRunner 之前执行;
*/
@Component
public class StartInitStartedEvent implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("StartInitStartedEvent:开始处理事情");
    }
}
/**
* 监听的是 ApplicationReadyEvent 事件,
* 则 ApplicationListener 一定会在 CommandLineRunner 和 ApplicationRunner 之后执行;
*/
@Component
public class StartInitReadyEvent implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        System.out.println("StartInitReadyEvent: 开始处理事情");
    }
}
实现 public interface ApplicationListener<E extends ApplicationEvent> 接口,监听 ApplicationEvent 及其下面的子事件:
/**
* 事件监听器一般是由开发者自己定义
* 定义事件监听器
*/
@Component
//@Lazy
public class MyApplicationListener implements ApplicationListener{

  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    event = (PayloadApplicationEvent)event;
    System.out.println(((PayloadApplicationEvent<?>) event).getPayload());
  }
}

@EventListener方式
将要执行的方法所在的类交个Spring容器扫描(@Component),并且在要执行的方法上添加@EventListener注解执行
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class EventListenerTest {
    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        log.info("启动时自动执行  @EventListener 注解方法");
    }
}

3、InitializingBean接口
@Component
@Data
public class TestInit implements InitializingBean,eanFactoryAware,ApplicationContextAware{
    private BeanFactory beanFactory:
    private ApplicationContext applicationContext;
    private String name;
@Override
public voidafterPropertiesSet(hrows Exception {
    System.out.printin("InIt heck");
}
}
Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用。
实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。先调用afterPropertieSet()方法,然后再调用init-method中指定的方法。

4、ApplicationRunner和CommandLineRunner
SpringBoot提供了两个接口来实现Spring容器启动完成后执行的功能,两个接口分别为CommandLineRunner和ApplicationRunner。这两个接口需要实现一个run方法,将代码在run中实现即可。这两个接口功能基本一致,其区别在于run方法的入参。ApplicationRunner的run方法入参为ApplicationArguments,为CommandLineRunner的run方法入参为String数组。
分页: 1/1 第一页 1 最后页 [ 显示模式: 摘要 | 列表 ]