开始通过ProdService将用户输入录入数据库……
SpringMVC是建立在Spring基础上的,除了SpringMVC,还有Springboot等框架。
Spring的一个核心功能,就是自动生成对象,或者说将对象的创建权交给Spring管理。
所以我们的代码可以写成这样:
public class RegisterControler { private IUserService userService; //没有赋值 @RequestMapping(method = RequestMethod.GET) public ModelAndView input(Model model) { userService.getByName("fg");如果没有Spring,userService没有被赋值,就会是null值,程序运行时就会报空指针异常(演示)
@Autowired private IUserService userService;
但什么类型的实例字段呢?演示:运行报错
需要在springmvc-servlet.xml中进行配置:
<bean class="srv.prod.UserService"></bean>
这样,Spring以后碰到IUserService的字段/变量,就会用srv.prod.UserService生成对象!这又被称之为解析(resolve)
@想一想@:freemarker的配置是怎么一回事?
演示:
@AutoWired还可以放置在构造函数、属性、方法参数上……
@Autowired public RegisterControler(IUserService userService) { this.userService = userService; }
实际开发中,会有很多很多需要注入的类,一个一个的配置会非常累。所以SpringMVC为我们提供了一种简便方式:
@Service public class UserService implements IUserService {
<context:component-scan base-package="srv.prod" />
为了有更好的语义,Spring还提供了:
<context:component-scan base-package="controllers" />
@想一想@:如何利用这个特性快速切换prod和mock service?
从SRV到BLL
演示生成:
因为独立了entity和model,所以必然产生以下转化(映射):
@Override public UserModel getByName(String name) { User user = userRepository.GetByName(name); UserModel userModel = new UserModel(); userModel.setId(user.getId()); userModel.setUsername(user.getUsername()); return userModel; }
@Override public int Register(RegisterModel model) { User user = new User(); user.setUsername(model.getUsername()); user.setPassword(model.getPassword()); userRepository.Save(user); return user.getId(); }
使用MapStruct的核心是声明这样的接口:
@Mapper public interface UserConvert { //接口中的所有字段都是隐式的public,static和final UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); User toUser(RegisterModel model); UserModel toUserModel(User entity); }
然后直接使用这个接口的INSTANCE字段,调用接口方法:
UserModel userModel = UserConvert.INSTANCE.toUserModel(user);你肯定会奇怪,这个接口都没有实现类的嘛?不,有的!演示:
public class UserConvertImpl implements UserConvert { @Override public User toUser(RegisterModel model) {
实现这种效果,依赖于:
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.4.2.Final</version> </dependency>
<m2e.apt.activation>jdt_apt</m2e.apt.activation>指示激活m2e-apt
<plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin>成功之后:
apt(annotatioin processing tool):它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。原理类似于Hibernate Criteria中的jpamodelgen。
//UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);而是在@Mapping上添加属性:
@Mapper(componentModel = "spring")然后利用@AutoWired注解由Spring依赖注且实例对象:
@Autowired private IUserRepository userRepository;默认映射规则(不需要任何额外注解):
Unmapped target properties: "id, inviteCode, invitedBy, invitedCode".
@Mapping()中常用自定义配置:
@Mapping(target = "pwd", source = "password")
User toUser(RegisterModel model); //将toUser()上的规则“倒”过来用 @InheritInverseConfiguration(name = "toUser") RegisterModel toRegister(User entity);
void toArticle(EditModel model, @MappingTarget Article article);和“生成”不同,unmapped字段会被target保留。@想一想@:为什么?对比:
EditModel model = new EditModel(); model.setTitle("model: 飞哥飞哥我爱你"); Article article = new Article(); article.setId(98); article.setTitle("article:就像老鼠爱大米"); //articleConvert.toArticle(model, article); article = articleConvert.toArticle(model); System.out.println(article.getTitle()); System.out.println(article.getId());
@Mapping(target = "id", ignore = true)
@Mappings({ @Mapping(target = "pwd", source = "password"), @Mapping(target = "id", ignore = true) })
Spring还集成了数据库访问功能。和ORM(比如Hibernate)集成的组件就是hibernate-orm。
需要通过maven引入:<dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.0.2.RELEASE</version> </dependency>其他hibernate相关依赖(包括mysql、连接池、缓存、log等)引入,略
在SpringORM中,引入了LocalContainerEntityManagerFactoryBean类,我们就可以直接(在repository中)通过依赖注入拿到
@Autowired private LocalContainerEntityManagerFactoryBean emFactoryContainer;然后通过LocalContainerEntityManagerFactoryBean就可以获得EntityManagerFactory:
EntityManagerFactory entityManagerFactory = emFactoryContainer.getNativeEntityManagerFactory(); //或者使用getObject()方法 EntityManagerFactory entityManagerFactory = emFactoryContainer.getObject();但从@Autowired就应该知道,LocalContainerEntityManagerFactoryBean是一个需要配置的SpringBean:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="PersistenceXmlLocation" value="/WEB-INF/persistence.xml" /> </bean>简便起见,我们直接利用PersistenceXmlLocation指定hibernate的persistence.xml配置文件路径,完成所有hibernate相关的配置。
演示:entityManagerFactory不会null
SpringMVC(和Hibernate一样)可以通过添加log4j.xml文件放到项目的Java Path目录下,适配log4j。
只是要在我们原来的log4j.xml中添加一个root logger配置才能记录Hibernate以外的信息:
<root level="INFO"> <AppenderRef ref="FileSpring" /> </root>
实际上log4j中,logger按树状结构组织,root就是根节点,所有日志信息都应该录入到root,除非子节点logger上声明了additivity="false"
<Logger name="org.hibernate.SQL" level="debug" additivity="false">演示查看:Spring的日志
@想一想@:每次请求,都会生成一个LocalContainerEntityManagerFactoryBean对象,再生成一个EntityManagerFactory,会不会造成巨大的性能浪费?
这个问题涉及到SpringBean生命周期,可选值包括:
所以LocalContainerEntityManagerFactoryBean默认就是singleton的,不用担心!
演示:
System.out.println("emFactoryContainer:"+ emFactoryContainer); System.out.println("-----------");
如何改变默认的scope呢?可以通过注解的方式指定
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Article extends Entity{
注意这里没有直接使用字符串,而是常量。另外两个web项目的scope由WebApplicationContext指定。
或者通过xml:
<bean id="entityManagerFactory" scope="prototype"
但是,比如我们在repository方法中验证,会发现不对劲!即使article的scope已经被设置为prototype,它在每次被调用时仍然是同一个对象?
@想一想@:为什么呢?因为:
我们自始至终都是在调用同一个repository对象的article字段,这个字段当然没法变化!
演示:将Article注入到scope标记为prototype的Controller中:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class RegisterControler {那如果我们就要在一个singleton的SpringBean中注入一个prototype的SpringBean呢?
一种办法是通过ApplicationContext的getBean()方法显式的获取SpringBean,触发scope=prototype机制
@Autowired private ApplicationContext context;
article = context.getBean(Article.class);
System.out.println("article:"+article);
还有另外一种方法,就是在指定scope的时候同时指定proxy(代理):
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)演示:proxyMode默认为NO/Default,也就是不使用代理。
所谓“使用代理”,和之前Hibernate的lazy load实现类似,就是:
所以,……
除了不代理(default和no),ScopedProxyMode还有两种:
多快好省!前端后端,线上线下,名师精讲
更多了解 加: