我正在尝试在spring boot下设置测试上下文,所以我定义了一个TestApplicationContext,如下所示:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {MvcConfig.class,SecurityConfig.class})
@WebAppConfiguration
@EnableAutoConfiguration
@PropertySource({ "classpath:persistence.properties" })
@ComponentScan
public class TestApplicationContext {
@Autowired
private Environment env;
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.setPort(9000);
factory.setSessionTimeout(10, TimeUnit.MINUTES);
//factory.addErrorPages(new ErrorPage(HttpStatus.404, "/notfound.html"));
return factory;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(hsqlDataSource());
em.setPackagesToScan(new String[] { "com.touchcorp.touchppoint.model" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(buildHibernateProperties());
return em;
}
@Bean
public DataSource hsqlDataSource()
{
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName(org.hsqldb.jdbcDriver.class.getName());
basicDataSource.setUsername("sa");
basicDataSource.setPassword("");
basicDataSource.setUrl("jdbc:hsqldb:mem:mydb");
return basicDataSource;
}
/**
*
* Loading all the hibernate properties from a properties file
*/
protected Properties buildHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
hibernateProperties.setProperty("hibernate.show_sql", "true");
hibernateProperties.setProperty("hibernate.use_sql_comments", "true");
hibernateProperties.setProperty("hibernate.format_sql", "true");
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
hibernateProperties.setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("javax.persistence.validation.mode", env.getProperty("javax.persistence.validation.mode"));
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", env.getProperty("org.hibernate.envers.store_data_at_delete"));
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", env.getProperty("org.hibernate.envers.global_with_modified_flag"));
return hibernateProperties;
}
}
哪个尝试建立自己的持久化上下文,所以我可以运行这样的测试:
@RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationIntegrationTest extends TestApplicationContext {
MockMvc mockMvc;
@Autowired
private WebApplicationContext wac;
@Resource(name="springSecurityFilterChain")
private FilterChainProxy springSecurityFilterChain;
@Before
public void setup() {
// using the web application to initiate the mock
mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilter(springSecurityFilterChain).build();
}
@Test
public void thatViewBootstrapUsesHttpNotFound() throws Exception {
MvcResult result = mockMvc.perform(post("/login")
.param("username", "user").param("password", "password")).andReturn();
Cookie c = result.getResponse().getCookie("my-cookie");
assertThat(c.getValue().length(), greaterThan(10));
// No cookie; 401 Unauthorized
mockMvc.perform(get("/")).andExpect(status().isUnauthorized());
// With cookie; 200 OK
mockMvc.perform(get("/").cookie(c)).andExpect(status().isOk());
// Logout, and ensure we're told to wipe the cookie
result = mockMvc.perform(delete("/session")).andReturn();
c = result.getResponse().getCookie("my-cookie");
assertThat(c.getValue().length(), is(0));
}
}
现在这一切都得到了一个hibernate HSQLDB userDao的支持,它由DeviceUsernamePasswordAuthenticationProvider调用
@Service(value="customAuthenticationProvider")
public class DeviceUsernamePasswordAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOG = LoggerFactory.getLogger(DeviceUsernamePasswordAuthenticationProvider.class);
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private DeviceDetailsService deviceDetailsService;
@Override
public boolean supports(Class<? extends Object> authentication) {
return authentication.equals(DeviceUsernamePasswordAuthenticationToken.class);
}
/**
* Implements authentication with a number of scenarios in mind:
* <ul>
* <li>A non-browser client is making a request without device details - FAIL</li>
* <li>A non-browser client is making a request with device details in the request - CHECK
* if successful a TOKEN is set</li>
* <li>A non-browser client is making a request with device TOKEN - CHECK</li>
* <li>A browser client is making a request with device TOKEN or device details with user and password - CHECK
* if successful JSESSIONID is set</li>
* </ul>
* @param authentication
* @return
* @throws AuthenticationException
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
LOG.info("Authenticating device and user - assigning authorities...");
DeviceUsernamePasswordAuthenticationToken auth = (DeviceUsernamePasswordAuthenticationToken) authentication;
String name = auth.getName();
String password = auth.getCredentials().toString();
boolean isDeviceRequest = (name == null && password == null);
LOG.debug("name is {}, password is {}", name, password);
// (a) nothing, (b) hasToken|<token encoding>, or (c) getToken|<base64 encoded device request>
String deviceToken = auth.getDeviceAuthorisation();
if (deviceToken == null) {
// very bad - set as anonymous
LOG.error("missing.device.token");
throw new BadCredentialsException("missing.device.token");
}
LOG.debug("deviceToken is {}", deviceToken);
String[] deviceInformation = StringUtils.split(deviceToken,"|");
DeviceDetails device = null;
if(deviceInformation[0].equals("getToken")) {
LOG.debug("getToken");
// we expect the array to be of length 3, if not, the request is malformed
if (deviceInformation.length < 3) {
LOG.error("malformed.device.token");
throw new BadCredentialsException("malformed.device.token");
}
device = deviceDetailsService.loadDeviceByDeviceId(deviceInformation[1]);
if (device == null) {
LOG.error("missing.device");
throw new BadCredentialsException("missing.device");
} else {
// otherwise, get the authorities
auth = new DeviceUsernamePasswordAuthenticationToken(null, null,
device.getDeviceId(), device.getAuthorities());
//also we need to set a new token into the database
String newToken = Hashing.sha256()
.hashString("your input", Charsets.UTF_8)
.toString();
deviceDetailsService.setToken(device.getDeviceId(),newToken);
// and put it into the response headers
auth.setDeviceTokenForHeaders(newToken);
}
} else if(deviceInformation[0].equals("hasToken")) {
LOG.debug("hasToken");
if (deviceInformation.length < 3) {
LOG.error("malformed.device.token");
throw new BadCredentialsException("malformed.device.token");
}
// check that there is a token and that the token has not expired
String token = deviceDetailsService.getToken(deviceInformation[1]);
if (token == null) {
// we got a token in the request but the token we have no stored token
LOG.error("mismatched.device.token");
throw new BadCredentialsException("mismatched.device.token");
} else if(!token.equals(deviceInformation[2])) {
// we got a token in the request and its not the same as the token we have stored
LOG.error("mismatched.device.token");
throw new BadCredentialsException("mismatched.device.token");
} else if ( deviceDetailsService.hasTokenExpired(deviceInformation[1])) {
// we got a token in the request and its not the same as the token we have stored
LOG.error("expired.device.token");
throw new BadCredentialsException("expired.device.token");
} else {
// token was in the request, correctly formed, and matches out records
device = deviceDetailsService.loadDeviceByDeviceId(deviceInformation[1]);
auth = new DeviceUsernamePasswordAuthenticationToken(null, null,
device.getDeviceId(), device.getAuthorities());
}
} else {
LOG.error("malformed.device.token");
throw new BadCredentialsException("malformed.device.token");
}
if (!isDeviceRequest) {
// OK, now we can do the user stuff
UserDetails user = customUserDetailsService.loadUserByUsername(name);
auth = new DeviceUsernamePasswordAuthenticationToken(name, password, device.getDeviceId(), device.getAuthorities());
}
return auth;
}
}
使用CustomUserDetailsService,如下所示:
@Service(value="customUserDetailsService")
@Transactional(readOnly=true)
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
private static final Logger LOG = LoggerFactory.getLogger(CustomUserDetailsServiceImpl.class);
@Autowired
private UserDao userDao;
public UserDetails loadUserByUsername(String login)
throws UsernameNotFoundException {
LOG.info("Finding the UserDetails for {}", login);
com.touchcorp.touchpoint.model.domain.User domainUser = userDao.getUser(login);
if(domainUser == null) {
throw new UsernameNotFoundException("The user {} was not found", login);
}
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
domainUser.getLogin(),
domainUser.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(domainUser.getRoles())
);
}
public Collection<? extends GrantedAuthority> getAuthorities(List<Role> roles) {
List<GrantedAuthority> authorities = new ArrayList<>();
LOG.debug("List of Authorities...");
for (Role role : roles) {
StringBuilder bob = new StringBuilder("ROLE_");
bob.append(role.getRole().toUpperCase());
LOG.debug("authority {}",bob.toString());
authorities.add(new SimpleGrantedAuthority(bob.toString()));
}
return authorities;
}
}
调用userDao ..
@Repository("userDao")
public class UserDaoImpl implements UserDao {
private static final Logger LOG = LoggerFactory.getLogger(UserDaoImpl.class);
@PersistenceContext
private EntityManager entityManager;
public User getUser(String login) {
LOG.debug("Looking for a user with login: {}", login);
List<User> list = entityManager.createQuery(
"SELECT u FROM User u WHERE u.login = :login")
.setParameter("login", login).getResultList( );
return (list.isEmpty( )?null:list.get( 0 ));
}
}
问题是容器无法启动,而是抛出邪恶的BeanCreationException。
这是跨越各个层的堆栈跟踪
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customAuthenticationProvider': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.touchcorp.touchpoint.service.CustomUserDetailsService com.touchcorp.touchpoint.security.DeviceUsernamePasswordAuthenticationProvider.customUserDetailsService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customUserDetailsService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.touchcorp.touchpoint.model.dao.UserDao com.touchcorp.touchpoint.service.CustomUserDetailsServiceImpl.userDao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1017)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
... 45 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.touchcorp.touchpoint.service.CustomUserDetailsService com.touchcorp.touchpoint.security.DeviceUsernamePasswordAuthenticationProvider.customUserDetailsService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customUserDetailsService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.touchcorp.touchpoint.model.dao.UserDao com.touchcorp.touchpoint.service.CustomUserDetailsServiceImpl.userDao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
... 56 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customUserDetailsService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.touchcorp.touchpoint.model.dao.UserDao com.touchcorp.touchpoint.service.CustomUserDetailsServiceImpl.userDao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1017)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
... 58 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.touchcorp.touchpoint.model.dao.UserDao com.touchcorp.touchpoint.service.CustomUserDetailsServiceImpl.userDao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
... 69 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:356)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1017)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
... 71 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:559)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:515)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:682)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:655)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:155)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:353)
... 82 more
有谁知道我在这里缺少什么?
答案 0 :(得分:1)
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {MvcConfig.class,SecurityConfig.class})
@WebAppConfiguration
这3行对你的配置类没有任何意义。它应该用@Configuration
注释,那3行应该在你的测试类上。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestApplicationContext.class)
@WebAppConfiguration
public class ApplicationIntegrationTest { … }
the testing部分的弹簧启动参考指南中也对此进行了解释。
答案 1 :(得分:0)
我讨论过回答我自己的问题,但我想如果我对原帖进行了编辑,那会让事情有点混乱。在beebajay的评论之后,我修改了TestApplicationContext和ApplicationIntegrationTest来读取:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {MvcConfig.class,SecurityConfig.class})
@WebAppConfiguration
@EnableAutoConfiguration
@PropertySource({ "classpath:persistence.properties" })
@ComponentScan
public class TestApplicationContext {
@Autowired
private Environment env;
// body same as before
}
和
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestApplicationContext.class,MvcConfig.class,SecurityConfig.class})
public class ApplicationIntegrationTest {
MockMvc mockMvc;
@Autowired
private WebApplicationContext wac;
@Resource(name="springSecurityFilterChain")
private FilterChainProxy springSecurityFilterChain;
// rest same as before
}
但是发生的事情是我已经退回一步并且ApplicationIntegrationTest不再编译,抱怨它无法解析名为“springSecurityFilterChain”的bean
我认为配置时暗示了这一点:
@Configuration
@EnableWebMvcSecurity
@ComponentScan({
"com.touchcorp.touchpoint.security",
"com.touchcorp.touchpoint.service",
"com.touchcorp.touchpoint.model.dao"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DeviceUsernamePasswordAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
@Order(2)
@Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=1")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/");
}
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
}
再次抱歉用一个问题回答我的问题,但如果我这样做是为了编辑,我认为它会变得非常混乱。