spring-day02

Spring-Day02

一、spring的注解开发和xml开发的优略势-老程序员喜欢问的面试题

优点:使用注解的形式替代xml配置,将繁杂的spring配置文件从工程中彻底消除掉,简化书写。

弊端:注解驱动无法在第三方开发的资源中进行编辑,因此会增大开发工作量

二、注解开发

1. 开启注解扫描-设置扫描的包

在applicationContext.xml 配置文件中开启注解扫描

<!--启动注解驱动,指定对应扫描的路径,也就是资源所在的包-->
<context:component-scan base-package="com.itheima"/>
  • 注:设置包扫描的时候范围尽可能的小

2. 在对象的类上添加对象的注解

  • controller层

    • @Controller
  • service层

    • @Service
  • dao层

    • @Repository
  • 除了以上三层以外的对象

    • @Component

Spring就是通过扫描指定包下的有这些注解的类,把它变成bean加入到IoC容器中,再通过ctx.getBean()获取出来使用。

在UserServiceImpl上加注解

@Component("userService")
public class UserServiceImpl implements UserService {
    private UserDao userDao;
    public void save() {
        System.out.println("UserServiceimpl 运行了....");
    }
}

使用测试类测试

public class AppTest2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) ctx.getBean("userService");
        userService.save();//可以正常执行
    }
}

3. 在类上添加了注解以后都会有一个默认的id,默认的id就是类名首字母小写,如果你觉得不好你可以自己去指定

4. 其他的注解

  • @Scope(类注解器)

    • 如果在类上不配置该注解默认为单例

      //设定bean的作用域
      @Scope("singleton")
      public class UserServiceImpl implements UserService {}
      
  • @PostConstruct(方法注解)在初始化方法上

    //设定bean的生命周期
        @PostConstruct
        public void init(){}
    
  • @PreDestroy(方法注解)销毁方法上

    //设定bean的生命周期
    @PreDestroy
    public void destroy(){}
    

5. 整合第三方的资源对象

    1. 编写一个方法返回一个核心对象
    1. 在方法上添加@Bean(“id”)
    1. 当前的类上添加一个注解
    • @Component

      在com.itheima.config中创建一个JDBCConfig类
      //添加Component注解
      @Component
      public class JDBCConfig {
      		//在方法上添加@Bean("id")
          @Bean("dataSource")
          public DruidDataSource getDataSource(){
              DruidDataSource ds = new DruidDataSource();
      
              ds.setDriverClassName("com.mysql.jdbc.Driver");
              ds.setUrl("jdbc:mysql://localhost:3306/Spring_db");
              ds.setUsername("root");
              ds.setPassword("root");
              return ds;
          }
      }
      

      在测试类中获取数据源成功执行

      DruidDataSource dataSource = (DruidDataSource) ctx.getBean("dataSource");
      System.out.println(dataSource);
      

说明:

  • 因为第三方bean无法在其源码上进行修改,使用@Bean解决第三方bean的引入问题

  • 该注解用于替代XML配置中的静态工厂与实例工厂创建bean,不区分方法是否为静态或非静态

  • @Bean所在的类必须被spring扫描加载,否则该注解无法生效

6. 属性注解

    1. 注入一个普通的数据
    • @Value

          @Value("100")
          private int num;
          public void save() {
              System.out.println("num=" + num);//结果输出:num=100
          }
      
    1. 注入一个对象属性
    • @Autowired

        1. Autowired注入的时候先按照类型进行匹配,如果类型能匹配上则直接注入,如果类型不能匹配(一个类有多个对象),则接着使用id进行匹配,id就是属性的名字

    @Autowired
    private UserDao userDao;
    public void save() {
    userDao.save();//这样就可以调用Dao了
    }
    ```

    - 2. 如果出现id也不能匹配的情况,则使用@Qualifier来区分
    
         ```java
         @Autowired
         @Qualifiler("userDao1")//指定加载的id
         private UserDao userDao;
         public void save() {
             userDao.save();//这样就可以调用Dao了
         }
         ```
    
    - 3. @Primary提交对象的使用优先级
    
    ```java
    @Component
    @Primary//优先使用这个实现类
    public class BookDaoImpl2 implements BookDao {
        public void save() {
            System.out.println("book dao running...2");
        }
    }
    ```
    
    • @Resource

      • 现在几乎不在使用,原因是jdk1.8以后不在支持该注解

7. 加载外部的properties配置文件

    1. 在类上添加@PropertySource
    1. 使用@Value("${key}")的形式去获取并注入给其他的属性
    @Component("userDao")
    @PropertySource("classpath:jdbc.properties")
    //有多个配置文件用下面数组形式,ignore为找不到忽略找不到文件错误提示
    @PropertySource(value={"classpath:jdbc.properties","classpath:abc.properties"},ignoreResourceNotFound = true)
    
    public class UserDaoImpl implements UserDao {
    
        @Value("${username}")
        private String username;
        @Value("${password}")
        private String password;
    
        public void save() {
            System.out.println("Userdao运行了......."+username+"   "+password);
        }
    }
    
  • 注意:

    • 不支持*通配格式,一旦加载,所有spring控制的bean中均可使用对应属性值
    • ignoreResourceNotFound:如果资源未找到,是否忽略,默认为false

8. 配置一个配置类并开启注解扫描

  • @Configuration

    • 添加在类上面的-用来标识当前的类就是一个配置类
  • @ComponentScan

    • 开启注解扫描的
    @Configuration
    @ComponentScan("com.itheima")
    public class SpringConfig {
    }
    
  • 测试类中创建使用AnnotationConfigApplicationContext

    ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserDao userDao = (UserDao) ctx.getBean("userDao");
    userDao.save();
    

9. 在配置类上通过@import注解引入另一个配置类

  • @Import({JDBCConfig.class})

    @Import(JDBCConfig.class)
    public class SpringConfig {
    }
    
    //导入多个使用
    @Import({JDBCConfig.class,ABC.class})
    
  • 说明:

    • 在被导入的类中可以继续使用@Import导入其他资源(了解)

    • @Bean所在的类可以使用导入的形式进入spring容器,无需声明为bean

10. bean的控制

    1. 普通对象创建的顺序-面试题
    • xml: xml在创建对象的时候会按照xml配置的顺序进行创建,如果你想先创建某一个对象你只需要调整他的配置的顺序即可

    • 注解: @DependsOn 标注的顺序

  • @DependsOn(“beanId”)

    @DependsOn("userService")//依赖userService,比userService后加载
    public class UserDaoImpl implements UserDao {
    }
    
    1. 对象创建的时机(延迟加载)
    • @Lazy
      • 位置:bean定义的位置(类上或方法上)
      • 控制bean的加载时机,使其延迟加载
    1. 配置对象的创建顺序
    • @Order(数字)

      @Order(2)
      public class SpringConfig {
      }
      
    • 数字越小优先级越高

三、使用注解的形式来整合mybatis

整合分析图:

image-20200730200332101

1. 导入jar包

2. 使用@Configuration来编写一个主配置类

@Configuration  //新建Springconfig类,在类上添加注解

3. 在主配置类上添加注解扫描

@ComponentScan("com.itheima")//扫描注解

4. 编写一个jdbc的配置并导入jdbc.properties配置文件

    1. 导入配置文件

      @PropertySource("classpath:jdbc.properties")
      
    1. 整合druid的核心配置类
      1. 从spring容器中获取jdbc的连接数据
      1. 编写一个方法创建druid的核心对象并返回同时在方法上添加@Bean(“dataSource”)
      //创建一个JDBCConfig 配置类,配置数据源
      public class JDBCConfig {
          @Value("${jdbc.driver}")
          private String driver;
          @Value("${jdbc.url}")
          private String url;
          @Value("${jdbc.username}")
          private String username;
          @Value("${jdbc.password}")
          private String password;
        
          @Bean("dataSource")
          public DataSource getDataSource(){
              DruidDataSource dataSource = new DruidDataSource();
              dataSource.setDriverClassName(driver);
              dataSource.setUrl(url);
              dataSource.setUsername(username);
              dataSource.setPassword(password);
              return dataSource;
          }
      }
      

5. 在主配置类中使用@import注解导入jdbc的配置类

//最终的Spring配置文件
@Configurable
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MybatisConfig.class})
public class SpringConfig {
}

6. 编写一个mybatis的配置类-创建2个对象

public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setDataSource(dataSource);
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        return ssfb;
    }
    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}

7. 在主配置类中使用@import注解导入mybatis的配置类

@Import({JDBCConfig.class,MybatisConfig.class})
public class SpringConfig {
}

8.Service层配置

@Service("accountService") //声明bean
public class AccountServiceImpl implements AccountService {
    //注入Dao对象
    @Autowired
    private AccountDao accountDao;
}

9.测试

public class AppTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        AccountService accountService = (AccountService) ctx.getBean("accountService");
        List<Account> all = accountService.findAll();
        System.out.println(all);
}

四、 spring整合junit做单元测试

1. 导入jar包

<!--junit-4.12-->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!--spring-test-->
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>

2. 在test目录下编写测试类并在类上添加2个注解

3. 在测试类里面使用@Autowired注入你想测试的类

//设定spring专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//设定加载的spring上下文对应的配置
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;
    @Test
    public void testFindById(){
        Account ser = accountService.findById(2);
        Assert.assertEquals("关羽",ser.getName());
    }
}

五、 spring排除某一些类和注解

1. 使用spring提供好的功能

//在Spring配置文件扫描器上面加excludeFilters过滤器
@ComponentScan(
value = "com.itheima",												//设置基础扫描路径
excludeFilters = @ComponentScan.Filter(				//设置过滤规则,excludeFilters为排除过滤
    type= FilterType.ANNOTATION,							//设置过滤器//设置过滤方式为按照注解进行过滤
    classes = Service.class										///设置具体的过滤项,过滤所有@Repository修饰的bean
    )
)
  • includeFilters:设置包含性过滤器
  • excludeFilters:设置排除性过滤器
  • ype:设置过滤器类型

2. 自定义过滤器

    1. 编写一个类实现TypeFilter接口
public class MyTypeFilter implements TypeFilter {
    @Override
    //加载的类满足要求,匹配成功
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //通过参数获取加载的类的元数据
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //通过类的元数据获取类的名称
        String className = classMetadata.getClassName();
        //如果加载的类名满足过滤器要求,返回匹配成功
        if(className.equals("com.itheima.service.impl.UserServiceImpl")){
            //返回true表示匹配成功,返回false表示匹配失败。
          	//此处仅确认匹配结果,不会确认是排除还是加入,排除/加入由配置项决定,与此处无关
            return true;
        }
        return false;
    }
}

    1. 在ComponentScan中配置
@ComponentScan(
    value = "com.itheima",
    excludeFilters = @ComponentScan.Filter(
            type= FilterType.CUSTOM,
            classes = MyTypeFilter.class
    )
)

3.自定义导入器

  • 1.新建自定义导入器类

    public class MyImportSelector implements ImportSelector { 
      public String[] selectImports(AnnotationMetadata icm) {
    				return new String[]{"com.itheima.dao.impl.AccountDaoImpl"}; 
      	}
    }
    
  • 在SpringConfig配置类上添加导入

    @Configuration 
    @ComponentScan("com.itheima") 
    @Import(MyImportSelector.class) //把自定义的导入器加载上来
    public class SpringConfig {
    }
    

4.自定义注册器

  • 实现创建一个类实现ImportBeanDefinitionRegistrar类

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { 
      		public void registerBeanDefinitions(AnnotationMetadata icm, BeanDefinitionRegistry r) {
    				ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(r, false); 
            TypeFilter tf = new TypeFilter() {
    						public boolean match(MetadataReader mr, MetadataReaderFactory mrf) throws IOException { 
                  return true;
    					}
            };
          scanner.addIncludeFilter(tf);
          scanner.scan("com.itheima"); }
    }
    
  • 导入自定义的注册器

    //自定义注册器就相当于@ComponentScan的功能
    @Import(MyImportBeanDefinitionRegistrar.class) //把自定义的注册器加载上来
    public class SpringConfig {
    }
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以通过在Spring Security中配置一个Token认证过滤器来实现基于WebSocket的Token认证。具体步骤如下: 1. 创建一个TokenAuthenticationFilter类,继承自OncePerRequestFilter并实现doFilterInternal方法。该类负责检查请求中是否含有效的Token,并进行相应的认证处理。 ```java public class TokenAuthenticationFilter extends OncePerRequestFilter { private final TokenProvider tokenProvider; public TokenAuthenticationFilter(TokenProvider tokenProvider) { this.tokenProvider = tokenProvider; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = getTokenFromRequest(request); if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) { Authentication authentication = tokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); } filterChain.doFilter(request, response); } private String getTokenFromRequest(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } } ``` 2. 创建一个TokenProvider类,用于生成Token和验证Token的有效性,并根据Token获取用户信息。 ```java @Component public class TokenProvider { private static final String SECRET_KEY = "my-secret-key"; private static final long EXPIRATION_TIME = 86400000; // 1 day public String generateToken(Authentication authentication) { UserPrincipal principal = (UserPrincipal) authentication.getPrincipal(); Date expirationDate = new Date(System.currentTimeMillis() + EXPIRATION_TIME); return Jwts.builder() .setSubject(Long.toString(principal.getId())) .setIssuedAt(new Date()) .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } public Authentication getAuthentication(String token) { Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); Long userId = Long.parseLong(claims.getSubject()); UserPrincipal principal = new UserPrincipal(userId); return new UsernamePasswordAuthenticationToken(principal, "", principal.getAuthorities()); } } ``` 3. 在配置类中注册TokenAuthenticationFilter和TokenProvider,并将TokenAuthenticationFilter添加到Spring Security的过滤器链中。 ```java @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Autowired private TokenProvider tokenProvider; @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS(); } @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(new ChannelInterceptorAdapter() { @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); if (StompCommand.CONNECT.equals(accessor.getCommand())) { String token = accessor.getFirstNativeHeader("Authorization"); if (StringUtils.hasText(token) && token.startsWith("Bearer ")) { token = token.substring(7); TokenAuthenticationFilter filter = new TokenAuthenticationFilter(tokenProvider); SecurityContextHolder.getContext().setAuthentication(filter.getAuthentication(token)); } } return message; } }); } @Override public void configureClientOutboundChannel(ChannelRegistration registration) { } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { } @Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { } @Override public boolean configureMessageConverters(List<MessageConverter> messageConverters) { return true; } @Override public void configureWebSocketTransport(WebSocketTransportRegistration registry) { } @Bean public TokenAuthenticationFilter tokenAuthenticationFilter() throws Exception { TokenAuthenticationFilter filter = new TokenAuthenticationFilter(tokenProvider); filter.setAuthenticationManager(authenticationManager()); return filter; } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()) .passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override @Bean public UserDetailsService userDetailsService() { return new UserDetailsServiceImpl(); } } ``` 在上述代码中,我们通过重写configureClientInboundChannel方法,在连接到WebSocket时获取请求中的Token,并使用TokenAuthenticationFilter进行认证。注意,我们需要将TokenAuthenticationFilter添加到Spring Security的过滤器链中,以便它能够在WebSocket连接期间对请求进行拦截。 最后,我们需要在客户端的连接请求中添加Authorization头部,以便在服务端进行Token认证。例如: ```javascript stompClient.connect({}, function(frame) { console.log('Connected: ' + frame); stompClient.subscribe('/topic/greetings', function(greeting) { showGreeting(JSON.parse(greeting.body).content); }); }, function(error) { console.log('Error: ' + error); }, {"Authorization": "Bearer " + token}); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值