Back

SpringBoot初体验

如非说明,皆是使用1.5

本文只进行简单的代码提示,具体内容可自行百度

CXF

@Bean
public ServletRegistrationBean CXFServlet() {
    ServletRegistrationBean cxfBean = new ServletRegistrationBean(new CXFServlet(), "/webservice/*");
    cxfBean.setName("CXFServlet");
    cxfBean.setLoadOnStartup(1);
    return cxfBean;
}

@Configuration
public class CXFConfig {
    @Autowired
    private Bus bus;
    @Autowired
    private SSOService sSOService;
	
    @Bean
    public Endpoint endpointSSO() {
        EndpointImpl endpoint = new EndpointImpl(bus, sSOService);
        endpoint.publish("/sso");
        return endpoint;
    }
	
}

错误页面配置

/**
 * 跳转至错误页面
 * add 2018.07.11
 * @author wm
 * @param code
 * @return
 */
@RequestMapping("/error/{code}")
public String goToErrorPage(@PathVariable(value="code") String code){
    return code;
}

新增拦截器:

/**
 * 错误页面拦截器
 * 替代EmbeddedServletContainerCustomizer在war中不起作用的方法
 * @author wm
 */
@Component
public class ErrorPageInterceptor extends HandlerInterceptorAdapter {
    private List<Integer> errorCodeList = Arrays.asList(404,500);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
        Exception {
       if (errorCodeList.contains(response.getStatus())) {
            response.sendRedirect(request.getContextPath()+"/error/" + response.getStatus());
            return false;
        }
        return super.preHandle(request, response, handler);
    }
}

新增拦截配置:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(errorPageInterceptor);//.addPathPatterns("/action/**", "/mine/**");默认所有
    super.addInterceptors(registry);
}

兼容JSP

pom新增:

<dependencies>
    ...
    <!--jsp支持 -->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>
    <!-- 配置jsp-jstl的支持 -->
    <dependency>
        <groupId>javax.servlet.jsp.jstl</groupId>
        <artifactId>jstl-api</artifactId>
        <version>1.2</version>
    </dependency>
    ...
</dependencies>

<build> 
    ...
    <resources>
        <resource>
            <directory>src/main/webapp</directory>
            <!-- 处理jar包启动无法访问jsp的问题 -->
            <targetPath>META-INF/resources</targetPath>
            <filtering>true</filtering>
            <includes>
                <include>**/**</include>
            </includes>
        </resource> 
    </resources>
</build>

新增配置类(处理jar包启动无法访问jsp的问题):

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.WebResourceRoot.ResourceSetType;
import org.springframework.util.ResourceUtils;

/**
 * Add main class fat jar/exploded directory into tomcat ResourceSet.
 *
 * @author hengyunabc 2017-07-29
 *
 */
public class StaticResourceConfigurer implements LifecycleListener {

    private final Context context;

    public StaticResourceConfigurer(Context context) {
        this.context = context;
    }

    @Override
    public void lifecycleEvent(LifecycleEvent event) {
        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            URL location = this.getClass().getProtectionDomain().getCodeSource().getLocation();

            if (ResourceUtils.isFileURL(location)) {
                // when run as exploded directory
                String rootFile = location.getFile();
                if (rootFile.endsWith("/BOOT-INF/classes/")) {
                    rootFile = rootFile.substring(0, rootFile.length() - "/BOOT-INF/classes/".length() + 1);
                }
                if (!new File(rootFile, "META-INF" + File.separator + "resources").isDirectory()) {
                    return;
                }

                try {
                    location = new File(rootFile).toURI().toURL();
                } catch (MalformedURLException e) {
                    throw new IllegalStateException("Can not add tomcat resources", e);
                }
            }

            String locationStr = location.toString();
            if (locationStr.endsWith("/BOOT-INF/classes!/")) {
                // when run as fat jar
                locationStr = locationStr.substring(0, locationStr.length() - "/BOOT-INF/classes!/".length() + 1);
                try {
                    location = new URL(locationStr);
                } catch (MalformedURLException e) {
                    throw new IllegalStateException("Can not add tomcat resources", e);
                }
            }
            this.context.getResources().createWebResourceSet(ResourceSetType.RESOURCE_JAR, "/", location,
                    "/META-INF/resources");

        }
    }
}

import org.apache.catalina.Context;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import cn.com.do1.component.config.assembly.StaticResourceConfigurer;

/**
 * 处理jar包启动无法访问jsp的问题
 * <p>Title: TomcatConfig</p>  
 * <p>Description: </p>  
 * @author wm  
 * @date 2018年7月24日
 */
@Configuration
@ConditionalOnProperty(name = "tomcat.staticResourceCustomizer.enabled", matchIfMissing = true)
public class TomcatConfig {
    @Bean
    public EmbeddedServletContainerCustomizer staticResourceCustomizer() {
        return new EmbeddedServletContainerCustomizer() {
            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                if (container instanceof TomcatEmbeddedServletContainerFactory) {
                    ((TomcatEmbeddedServletContainerFactory) container)
                            .addContextCustomizers(new TomcatContextCustomizer() {
                                @Override
                                public void customize(Context context) {
                                    context.addLifecycleListener(new StaticResourceConfigurer(context));
                                }
                            });
                }
            }

        };
    }
}

静态资源

方式一:
pom新增配置:

<resources>
    <resource>
        <directory>src/main/webapp</directory>
        <!--注意此次必须要放在此目录下才能被访问到-->
        <targetPath>META-INF/resources</targetPath>
        <filtering>true</filtering>
        <includes>
            <include>**/**</include>
        </includes>
    </resource>
</resources>

新增配置:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/js/**").addResourceLocations("classpath:/META-INF/resources/WEB-INF/js/");
    registry.addResourceHandler("/css/**").addResourceLocations("classpath:/META-INF/resources/WEB-INF/css/");
    registry.addResourceHandler("/images/**").addResourceLocations("classpath:/META-INF/resources/WEB-INF/images/");
    registry.addResourceHandler("/common/**").addResourceLocations("classpath:/META-INF/resources/WEB-INF/common/");
    registry.addResourceHandler("/register/**").addResourceLocations("classpath:/META-INF/resources/WEB-INF/register/");
    registry.addResourceHandler("/plugin/**").addResourceLocations("classpath:/META-INF/resources/WEB-INF/plugin/");
    super.addResourceHandlers(registry);
}

方式二(推荐):
将静态资源文件放到resouce/static目录下

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/");
    registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/");
    registry.addResourceHandler("/images/**").addResourceLocations("classpath:/static/images/");
    registry.addResourceHandler("/register/**").addResourceLocations("classpath:/static/register/");
    registry.addResourceHandler("/plugin/**").addResourceLocations("classpath:/static/plugin/");
    super.addResourceHandlers(registry);
 }

文件上传

现象:上传文件返回异常java.lang.ClassCastException: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile cannot be cast to org.springframework.web.multipart.commons.CommonsMultipartFile
原因:FileUtil中multipartToFile的代码片段 CommonsMultipartFile cf = (CommonsMultipartFile)multfile 与springboot自带的org.springframework.web.multipart.MultipartFile冲突

import org.springframework.web.multipart.commons.CommonsMultipartResolver;//这是旧项目引入的  
import org.springframework.web.multipart.MultipartFile;//这是springboot整合的  

解决:采用输入流方式获取File对象,具体代码自行百度

配置文件新增:

#upload
spring.http.multipart.enabled=true
spring.http.multipart.file-size-threshold=0  #0-ALLFILE
spring.http.multipart.location=D:/fsrzfw/temp
spring.http.multipart.max-file-size=10Mb
spring.http.multipart.max-request-size=10Mb

新增配置

/**
 * 配置上传文件大小的配置
 * @return
 */
@Bean
public MultipartConfigElement multipartConfigElement() {
   MultipartConfigFactory factory = new MultipartConfigFactory();
   //  单个数据大小
   factory.setMaxFileSize("102400KB");
   /// 总上传数据大小
   factory.setMaxRequestSize("102400KB");
   return factory.createMultipartConfig();
}

/**
 * MultipartFile 转换成File
 * update by wm 2018.07.12
 * @param multfile 原文件类型
 * @return File
 * @throws IOException
 */
public static File multipartToFile(MultipartFile multfile) throws IOException {
    //Springboot自带上传不支持CommonsMultipartFile
    //CommonsMultipartFile cf = (CommonsMultipartFile)multfile;
    //这个myfile是MultipartFile的
    //DiskFileItem fi = (DiskFileItem) cf.getFileItem();
    //return fi.getStoreLocation();

    File f = null;
    if ("".equals(multfile) || multfile.getSize() <= 0) {
        multfile = null;
    } else {
        InputStream ins = multfile.getInputStream();
        f = new File(multfile.getOriginalFilename());
        StreamUtil.streamSaveAsFile(ins, f);
    }
    return f;
}

打JAR包运行

pom新增配置:

<build>
    <finalName>fsrzfw</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>cn.com.do1.component.Application</mainClass>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
            <dependencies>
                <!-- spring热部署 -->
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>springloaded</artifactId>
                    <version>1.2.6.RELEASE</version>
                </dependency>
            </dependencies>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <configuration>
                <nonFilteredFileExtensions>
                    <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                    <nonFilteredFileExtension>woff</nonFilteredFileExtension>
                    <nonFilteredFileExtension>woff2</nonFilteredFileExtension>
                </nonFilteredFileExtensions>
            </configuration>
        </plugin>
    </plugins>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/**</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resource</directory>
            <includes>
                <include>**/**</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/webapp</directory>
            <!-- 处理jar包启动无法访问jsp的问题 -->
            <targetPath>META-INF/resources</targetPath>
            <filtering>true</filtering>
            <includes>
                <include>**/**</include>
            </includes>
        </resource> 
    </resources>
</build>

Shiro 自定义 filter 匹配异常,无限拦截重定向

参考:Shiro 自定义 filter 匹配异常
原因:自定义Filter注册为了 Bean交给 Spring托管,它会被自动注册到 FilterChain中。请求先经过自定义Filter,导致请求被其先消费掉了,而ShiroFilter成了摆设。
解决:FUN1、利用 FilterRegistrationBean 注册自定义 Filter (建议使用)

ex:
    @Bean
    public CasFilter casFilter(){
        CasFilter cf = new CasFilter();
        cf.setSuccessUrl(successUrl);
        cf.setFailureUrl(failureUrl);
        return cf;
    }
	
    /**
     * 注册casFilter
     * @param casFilter
     * @return
     */
    @Bean
    public FilterRegistrationBean registCasFilter(CasFilter casFilter) {
        FilterRegistrationBean cas = new FilterRegistrationBean();
        cas.setFilter(casFilter);
        cas.setEnabled(false);	//该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理  x
        return cas;
    }
	
    /**
     * 注册shiroFilter
     * @param securityManager
     * @param casFilter
     * @param logoutFilter
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager,
            CasFilter casFilter,LogoutFilter logoutFilter) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        ...
        Map<String,Filter> filters = new HashMap<>();
        filters.put("casFilter",casFilter);
        filters.put("logoutFilter", logoutFilter);
        shiroFilterFactoryBean.setFilters(filters);		//添加casFilter到shiroFilter
        ...
    }

FUN2、将 CasFilter注册为了 Bean交给 Spring托管,它会被自动注册到 FilterChain中,那我们如果不把它注册为 Bean就可以避免这个问题了。

    ex:
    /**
     * 注册shiroFilter
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        ...
        Map<String,Filter> filters = new HashMap<>();
        filters.put("casFilter",new CasFilter());
        filters.put("logoutFilter", new LogoutFilter());
        shiroFilterFactoryBean.setFilters(filters);		//添加casFilter到shiroFilter
        ...
    }

日志

现象:启动报错Caused by: java.lang.NoClassDefFoundError: ch/qos/logback/classic/turbo/TurboFilter
原因:springboot1.3.x和1.3.x以下版本才支持log4j的日志配置,1.3.x以上版本只支持log4j2和logback的日志配置
解决:使用log4j2或logback

jar包运行获取某个包下的class对象集合

现象:项目mvn install后jar包,启动报错,未获取到某个包下的class对象集合
原因:springboot项目打包后获取包资源所在路径与启动main不一样
ex://jarPaht:file:/E:/IDEABuilder/fs/tyrz-springboot/tyrz-front/target/fsrzfw.jar!/BOOT-INF/classes!/cn/com/do1/component/identitySource/service/impl
解决:逐一分割路径获取类对象

/**
 * 获得包下面的所有的class
 * @author FengHuayuan
 * @date 2018年4月21日 下午12:25:20.
 * @param pack
 * @return
 */
public static List<Class<?>> getClassesFromPackage(String pack) {
    List<Class<?>> clazzs = new ArrayList<Class<?>>();
    // 是否循环搜索子包
    boolean recursive = true;
    // 包名字
    String packageName = pack;
    // 包名对应的路径名称
    String packageDirName = packageName.replace('.', '/');
    Enumeration<URL> dirs;
    try {
        dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
        while (dirs.hasMoreElements()) {
            URL url = dirs.nextElement();
            String protocol = url.getProtocol();
            if ("file".equals(protocol)) {
                log.debug("*****【File类型】的扫描!");
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                findClassInPackageByFile(packageName, filePath, recursive, clazzs);
            } else if ("jar".equals(protocol)) {
                log.debug("*****【Jar类型】的扫描!");
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                getClasssFromJarFile(filePath, clazzs);//wm add
            }
        }

    } catch (Exception e) {
        log.error(e.getMessage(),e);
    }
    return clazzs;
}

/** 
 * 从jar文件中读取指定目录下面的所有的class文件 
 * springboot打jar包专用
 * @author wm
 * @param jarPaht   jar文件存放的位置 
 * @param filePaht    指定的文件目录 
 * @param clazzs 所有的的class的对象 
 */  
public static void getClasssFromJarFile(String jarPath,List<Class<?>> clazzs) {  

    log.info("getClasssFromJarFile - jarPath:"+jarPath);//wm
    //jarPaht:file:/E:/IDEABuilder/fs/tyrz-springboot/tyrz-front/target/fsrzfw.jar!/BOOT-INF/classes!/cn/com/do1/component/identitySource/service/impl
    String[] jarPaths = jarPath.split("!");
    String jarPaht=jarPaths[0].substring(6);//去掉file:/
    String startDir = (jarPaths[1]+jarPaths[2]).substring(1);//去掉/
	
    JarFile jarFile = null;  
    try {  
        jarFile = new JarFile(jarPaht);  
    } catch (IOException e1) {  
        e1.printStackTrace();  
    }  
  
    List<JarEntry> jarEntryList = new ArrayList<JarEntry>();  
  
    Enumeration<JarEntry> ee = jarFile.entries();  
    while (ee.hasMoreElements()) {  
        JarEntry entry = (JarEntry) ee.nextElement();  
        if (entry.getName().startsWith(startDir) && entry.getName().endsWith(".class")) {  
            log.info("getClasssFromJarFile - entry:"+entry.getName());//wm
            jarEntryList.add(entry);  
        }  
    }  
    for (JarEntry entry : jarEntryList) {  
        String className = entry.getName().replace('/', '.');  
        //   BOOT-INF/classes/cn/com/do1/component/identitySource/service/impl/XXX.class
        className = className.substring(17, className.length() - 6);//去掉  BOOT-INF/classes/     .class
        try {  
            clazzs.add(Thread.currentThread().getContextClassLoader().loadClass(className));  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        }  
    }  
  
}

诡异的异常

启动项目报错ClassNotFoundException: javax.ws.rs.core.Response$StatusType
解决:pom引入jsr311-api(g:javax.ws.rs)(a:jsr311-api)(v:1.1.1)

comments powered by Disqus