面试问答链接:https://mp.weixin.qq.com/s/Y17S85ntHm_MLTZMJdtjQQ
1. 实例是什么?bean是什么?
- new Student() 就是一个student类的一个实例。spring中的bean本质就是一个对象实例(new Student()),只不过取bean是spring管理的对象实例,它可以是任务java类的实例对象。
2. Spring包含哪些模块?
1. Spring Context:这个模块扩展了Spring Core,提供了更全面的编程和配置模型。它包含了Bean的生命周期管理,事件处理,资源加载,国际化等功能。
2. Spring AOP:面向切面编程(AOP)模块提供了面向切面的编程实现,允许开发者定义横切关注点,如日志、事务管理等。
3. Spring DAO:数据访问对象(DAO)模块提供了对JDBC的支持,并简化了数据库操作。
4. Spring ORM:对象关系映射(ORM)模块提供了对常见的对象关系映射框架(如Hibernate)的集成。
5. Spring Web:这个模块提供了对Web应用的支持,包括Servlet监听器、请求和响应处理等。
6. Spring Web MVC:这是Spring的MVC(Model-View-Controller)实现,用于构建Web应用程序。它提供了模型、视图和控制器的实现,以及URL路由和请求处理等功能。
3. Spring的IOC和AOP的理解?
- 推荐视频讲解:https://www.bilibili.com/video/BV1w3411s7ur?p=5&vd_source=d895293f5f20ef79bd6ffbeb4865aae9
- IOC-控制反转 它是一种思想。一句话来讲就是将new 对象这个事情交给了spring容器去管理。早期用户新增需求是依赖我们程序员去修改代码,现在交给了spring容器去管理,所以由程序员反转成用户了。
- IO-依赖注入。它是IOC的一种实现。通过构造器注入,属性注入,setter方式将对象注入到类中。
- AOP-面向切面编程,底层是通过动态代理实现(由jdk动态代理需要代理对象提供接口,cglib则不需要接口)。生产中我们一般用于日志打印。
- https://www.yuque.com/hollis666/wty0im/nget4r5wl2imegi7
- 日志打印:自定义注解,然后写一个切面类,其中通过@PointCut指定注解类名位置;使用@Before,@afterRetuing修饰方法,输出日志。
自定义注解:
定义切面:
4. 动态代理? 动态代理原理
- 什么是代理?什么是动态代理?什么是静态代理?
- 代理指的是,给一个对象提供额外的对象,由这个额外的对象去帮原对象执行行为。比如代理律师,婚介所代理人。
- 动态代理指的是利用反射获取代理对象,并在程序运行时才生产代理对象。
- 静态代理指的是在程序运行之间通过创建代理类重写原对象的方法进行对象的行为。
实现动态代理的核心:1.实现一个InvocationHandler处理类,在invoke实现对象的行为(比如打印日志,权限判断)2.利用proxy.newProxyInstance(反射获取代理对象,InvocationHandler)。
动态代理的应用场景?
- 实现aop-面向切面编程。
- 生产中用动态代理实现打印日志,权限控制。
- 打印日志:
//用户管理接口
public interface UserManager {
void createUser(String username, String password);
String getUserInfo(String username);
}
//用户管理实现类
public class UserManagerImpl implements UserManager {
@Override
public void createUser(String username, String password) {
//创建用户的实际逻辑
System.out.println("用户已创建: " + username);
}
@Override
public String getUserInfo(String username) {
//获取用户信息的实际逻辑
return "用户信息: " + username;
}
}
//日志记录处理器
public class LogHandler implements InvocationHandler {
private Object target;
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Logger logger = new Logger();
logger.log("调用方法: " + method.getName());
Object result = method.invoke(target, args);
logger.log("方法调用完成: " + method.getName());
return result;
}
}
//客户端代码
public class Client {
public static void main(String[] args) {
UserManager userManager = new UserManagerImpl();
//创建动态代理
UserManager proxy = (UserManager) Proxy.newProxyInstance(
UserManager.class.getClassLoader(),
new Class[]{UserManager.class},
new LogHandler(userManager)
);
//创建用户(会自动记录日志)
proxy.createUser("Alice", "password123");
//获取用户信息(会自动记录日志)
String userinfo = proxy.getUserInfo("Alice");
}
}
- 控制权限:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 接口定义
interface UserService {
void deleteUser();
}
// 实际的服务类
class UserServiceImpl implements UserService {
public void deleteUser() {
System.out.println("Deleting user...");
}
}
// 权限控制的 InvocationHandler
class PermissionInvocationHandler implements InvocationHandler {
private Object target;
public PermissionInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 模拟权限控制,这里简单地判断是否有删除用户的权限
if (method.getName().equals("deleteUser")) {
// 检查权限
if (checkPermission()) {
return method.invoke(target, args);
} else {
System.out.println("Permission denied.");
return null;
}
} else {
// 对于其他方法直接调用目标对象的方法
return method.invoke(target, args);
}
}
private boolean checkPermission() {
// 这里简单返回 true,实际情况根据权限逻辑来判断
return true;
}
}
public class Main {
public static void main(String[] args) {
// 创建实际的服务对象
UserService userService = new UserServiceImpl();
// 创建权限控制的 InvocationHandler
PermissionInvocationHandler handler = new PermissionInvocationHandler(userService);
// 创建动态代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
handler
);
// 调用代理对象的方法
proxy.deleteUser();
}
}
5. Spring注入Bean的几种方式?
- 以下三种方式。
- 构造器注入会出现什么问题?循环依赖问题。 https://www.yuque.com/hollis666/wty0im/lbst9ffoy74od6kr
- 可以通过@lazy延迟初始化。
6. 构造器的循环依赖问题
- spring无法解决两个bean都是通过构造器注入导致的循环依赖问题。
- 原因是 bean实例化构造器是最先执行的,而此时构造器注入的对象还没有实例化,所以无法解决循环依赖问题。
- 解决方式:1.使用@lazy注解 延缓初始化 2.使用属性注入或者set注入。
7. Bean的生命周期?
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public MyBean myBean() {
return new MyBean();
}
@Bean
public CustomBeanPostProcessor customBeanPostProcessor() {
return new CustomBeanPostProcessor();
}
}
1. 实例化(Instantiation):1.当我们使用@Bean创建一个bean时,首先根据对象的构造方法进行实例化。
public class MyBean {
private String name;
public MyBean() {
System.out.println("Bean实例化阶段:调用构造函数");
}
public void init() {
System.out.println("Bean 初始化...");
}
public void destroy() {
System.out.println("Bean 销毁...");
}
public void setName(String name) {
System.out.println("Bean 的属性赋值");
this.name = name;
}
public String getName() {
return name;
}
}
2. 属性赋值(Property Setting):在Bean实例化完成后,通过set方法或者注解(如@Autowired、@Resource等)对bean对象进行属性赋值。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Before Initialization - Bean Name: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("After Initialization - Bean Name: " + beanName);
return bean;
}
}
3. 初始化(Initialization):在属性赋值完成后,Spring IoC容器会调用Bean的初始化方法。可以通过实现InitializingBean接口的afterPropertiesSet()方法,或者通过@Bean中指定initMethod 初始化方法。
- 在执行初始化之前还可以通过BeanpostProcessor方法进行出初始化的before,after方法,处理一些前置逻辑。
4. 使用(In Use):在初始化完成后,Bean就可以被应用程序使用了。在这个阶段,Bean会被注入到其他Bean中,或者通过Spring容器获取并调用其方法。
5. 销毁(Destruction):当Bean不再需要时,Spring IoC容器会负责销毁Bean。可以通过实现DisposableBean接口的destroy()方法,或者通过@Bean注解指定destroyMethod定义销毁方法。在这个阶段,可以进行一些资源清理和回收的操作。
//输出结果
"Bean实例化阶段:调用构造函数"
"Bean 的属性赋值"
"Before Initialization - Bean Name:"
"Bean 初始化..."
"After Initialization - Bean Name: "
"Bean 销毁..."
8. 生产中写过@Bean吗?
9. 什么是钩子函数呢?有写过吗?
- 在 Spring 中,"钩子函数"通常指的是在 bean 的生命周期中可以被回调的特定方法。也就是bean生命周期中的InitializingBean接口的afterPropertiesSet()方法,beanpostProcessor中的after和before方法,disposablebean接口的distory方法。
10. Trancational注解的作用?
- Trancational注解表示开启一个事务。底层是基于aop实现的。
- java中提供两种实现事务的方式。1.@Trancational(不推荐)
@Transactional
public void test(){
//事务操作
}
2.编程式事务(推荐)
public void test(){
TransactionDefinition def = new DefaultTransactionmDefinition();
TransactionStatus status = transactionManager.getTransaction(def)
try{
// 事务操作
//事务提交
transactionManager.commit(status);
} catch (DataAccessException e) {
//事务提交
transactionManager.rollback(status);
throw e;
为什么不推荐使用@Trancational?
- 文章推荐:https://www.yuque.com/hollis666/wty0im/sscz8razzyxltzhe
- 虽然@Trancational没有侵入性,但是最小粒度只能作用在方法上,同时使用@Transactional不当会导致事务失效。
11. Spring的事务传播机制?
- 文章推荐:https://www.yuque.com/hollis666/wty0im/ixgoek25ybmy7ws4
- 事务的传播机制指的是控制多个事务之间相互调用的行为。
- Spring的事务规定了7种事务的传播级别,默认的传播机制是REQUIRED(required)
- REQUIRED,如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,总是只有一个事务在执行
- REQUIRESNEW,每次执行新开一个事务
- SUPPORTS,有事务则加入事务,没有事务则普通执行
- ·NOT SUPPORTED,有事务则暂停该事务,没有则普通执行
- MANDATORY,强制有事务,没有事务则报异常
- ·NEVER,有事务则报异常
- ·NESTED,如果之前有事务,则创建嵌套事务,嵌套套事务回滚不影响父事务,反之父事务影响嵌套事务.
场景题
- 1.@Transactional方法a和@Transactional方法b相互调用时,如果某一个发生异常,则保证都要回滚,如何写?
- 都是用默认的required级别即可。
@Service
public class TransactionFooService {
@Autowired
private FooDao fooDao;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() throws Exception {
// do something
fooDao.updateFoo();
}
}
@Service
public class TransactionBarService {
@Autowired
private BarDao barDao;
@Autowired
private TransactionFooService transactionFooService;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodB() throws Exception {
// do something
barDao.updateBar();
transactionFooService.methodA();
}
}
12. 常见的事务失效的场景有那些?
文章推荐:https://www.yuque.com/hollis666/wty0im/bz0tulziboigw24b
注意:一般指的都是使用@Trancational注解导致事务失效的场景。
13. @Trancational注解待整理!
1.同一个类中:
- 没有事务@Trancational注解的方法A直接调用还是this关键字调用有@Trancational方法B,则方法B的事务会失效。
@Service
public class MyService {
public void A() {
// do something
this.B();
B();
}
@Transactional //注解会失效
public void B() {
// do something
}
}
- 原因:没有走代理对象。
- 解决方式:1.将方法b写到另一个bean中。2.将此类作为属性注入,使用注入的对象调用方法b 3. 使用aopcontext.countproxy()强转当前类(需要引入aop依赖和开启代理对象暴露)
- 如果有@Trancational注解的方法A调用 无论是否有注解@Trancational的方法B时,方法B的注解@Trancational会失效。但是B会加入A方法的事务中。
@Service
public class MyService {
@Transactional
public void A() {
// do something
this.B(); //
}
@Transactional //注解会失效 但是方法B会加入到A的事务中
public void B() {
// do something
}
}
总结:同一个类中只要方法中相互调用则@Transactional 都会失效。
1.代理失效的情况:
- 1.在private修饰的方法上使用@Trancational导致失效,本质是无法用代理对象。
- 2.使用final,static修饰的方法也会导致@Trancational失效。
- 3.一个非@Trancational注解的方法,调用@Trancational注解的方法时,会导致事务失效。
思考如果@Trancational注解的方法调用另一个@Trancational注解的方法呢? - 如果A加@Trancational注解,B加不加@Transactional注解,事务是有效的,则AB在同一事务中。
2.使用@Trancational不当导致事务失效。
- @Trancational默认是error和runtimeException捕获异常,如果不设置Exception则非运行时异常就会导致捕获失败,从而导致事务失效。
public class MyService {
@Transactional(rollbackFor = RuntimeException.class)//发生runtimeException时,事务不会回滚
private void doInternal() {
System.out.println("Doing internal work...");
}
}
3.使用事务的方法中异常被捕获导致失效。
public class MyService {
@Transactional
public void doSomething() {
try {
doInternal();
} catch (Exception e) {
logger.error(e);
}
}
}
13. Spring事务的底层实现原理?
- Spring事务的底层实现原理是基于AOP和事务管理器的结合,通过代理模式和声明式事务管理来实现对事务的管理和控制。
14. Spring常用注解有哪些?
Web:
- @Controller:组合注解(组合了@Component注解),应用在MVC层(控制层)。
- @RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
- @RequestMapping:用于映射Web请求,包括访问路径和参数。如果是Restful风格接口,还可以根据请求类型使用不同的注解:
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @ResponseBody:支持将返回值放在response内,而不是一个页面,通常用户返回json数据。
- @RequestBody:允许request的参数在request体中,而不是在直接连接在地址后面。
- @PathVariable:用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值name,通常作为Restful的接口实现方法。
@GetMapping("/books/{category}/{id}")
public String getBookByCategoryAndId(@PathVariable String category, @PathVariable Long id) {
// 方法体
}
容器:
- @Component:表示一个带注释的类是一个“组件”,成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。
- @Service:组合注解(组合了@Component注解),应用在service层(业务逻辑层)。
- @Repository:组合注解(组合了@Component注解),应用在dao层(数据访问层)。
- @Autowired:Spring提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入)。
- @Qualifier:该注解通常跟 @Autowired 一起使用,当想对注入的过程做更多的控制,@Qualifier 可帮助配置,比如两个以上相同类型的 Bean 时 Spring 无法抉择,用到此注解。@Qualifier(“beanOne”)
@Component("beanOne")
class BeanOne implements Bean{}
@Component("bean Two")
class BeanTwo implements Bean {}
- @Configuration:声明当前类是一个配置类(相当于一个Spring配置的xml文件)
- @Value:可用在字段,构造器参数跟方法参数,指定一个默认值,支持 #{} 跟 ${} 两个方式。一般将 SpringbBoot 中的 application.properties 配置的属性值赋值给变量。
- ${}用于获取配置文件中的数据。#{}用于程序运行时进行复杂逻辑处理。
- 假设有一个名为 application.properties 的配置文件:
app.name=MyAppapp.version=1.0
可以使用 @Value 注解将配置文件中的属性值注入到 Bean 中:
@Componentpublic class MyComponent {
@Value("${app.name}")
private String appName;
@Value("${app.version}")
private String appVersion;
// 其他方法
}
- @Bean:注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。
- @Scope:定义我们采用什么模式去创建Bean(方法上,得有@Bean) 其设置类型包括:Singleton 、Prototype、Request 、 Session、GlobalSession。
AOP:
- @Aspect:声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
- @After :在方法执行之后执行(方法上)。
- @Before:在方法执行之前执行(方法上)。
- @Around:在方法执行之前与之后执行(方法上)。
- @PointCut:声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)。
事务:
- @Transactional:在要开启事务的方法上使用@Transactional注解,即可声明式开启事务。
15. @Autowired和@Resource的区别?
- 外部解答:https://www.yuque.com/hollis666/wty0im/gai6a9
- 相同点都是将bean注入到该类中。不同点:主要的区别就是@Autowired会先按照注入字段的类型进行匹配,如果有多个或者找不到再按照字段名进行匹配。而@Resource相反,先按照字段名,之后再按照类型匹配。
16. 为什么spring不建议使用基于字段的依赖注入呢?
- 外部解答:https://www.yuque.com/hollis666/wty0im/lbst9ffoy74od6kr
- 也就是我们平常使用的@Autowired priavte BaseConfig baseconfig,推荐使用构造器注入。
- 原因有:1.违背了单一职责原则(一个类只有一件功能)2.对于静态代码中通过这个基于字段注入会导致空指针。
17. Component与Bean的区别?
- @Component作用于类上,将这个类作为一个组件bean注入到spring容器中。而Bean作用于方法上,相对于@Component更精细化,比如实例的创建,销毁,初始化的前置后置操作。
18. BeanFactory、FactoryBean和ApplicationContext的区别?
- BeanFactory用于管理bean的定义和初始化。FactoryBean是一个特殊的bean,通过实现FactoryBean定制化复杂的bean对象。ApplicationContext是spring提供的容器,用来获取bean。
19. Spring循环依赖问题如何解决?常见的解决方案?
- Spring循环依赖指的是两个或者多个bean相互依赖对方形成一个循环,导致同时构建bean时会有问题。所以spring采用了三级缓存来解决循环依赖,本质是将bean的实例化与属性赋值初始化进行了分离。
- 使用三级缓存,或者使用@lazy延迟初始化来解决部分循环依赖问题。
20. 三级缓存是什么?主要为了干什么?二级缓存能否解决?
- 视频讲解:https://yq0pkza686.feishu.cn/minutes/obcn9y12ewhlm4ga2nv672el?from=from_copylink
- 三级缓存指的是spring提供的三个map。其中一级缓存存放完整初始化的bean对象,二级缓存存到半成品也就是仅实例化的对象,三级缓存存放的时单例bean的创建工厂(通过工厂可以实例化bean)。
- 三级缓存解决循环以来的过程:
- 1.a b相互引用。 a先实例化-将a的bean工厂放到三级缓存中-尝试注入b-b实例化-b中尝试获取a-从一级缓存中拿初始化的a-没有时从二级中拿-也没有就从三级中拿bean实例化a-并放到二级缓存中-初始化b-注入到a中-a初始化完成-a放到一级缓存中
二级缓存能解决循环依赖吗?能,但是对于违背了aop的设计原则,也就是在bean初始化执行完之后才生成代理对象。
- 1.a b相互引用。 a先实例化-将a的bean工厂放到三级缓存中-尝试注入b-b实例化-b中尝试获取a-从一级缓存中拿初始化的a-没有时从二级中拿-也没有就从三级中拿bean实例化a-并放到二级缓存中-初始化b-注入到a中-a初始化完成-a放到一级缓存中
21. Spring中单例Bean的线程安全问题?
视频推荐:https://k1dy9adkxea.feishu.cn/minutes/obcnwx4i8v8dgm5tzf97w413?from=from_copylink
- 单例作用域:成员变量和静态变量都是线程不安全的。因为单例整个应用程序只会存在一个bean,第一次创建之后后面都会复用。所以多线程并发调用就会导致数据被多个线程修改。
- 原型作用域:成员变量是线程安全的,静态变量是线程不安全的。因为每次请求都会生成新的bean对象,所以多线程修改数据是会不影响的。
那么如何解决单例作用域下的成员变量和静态变量不安全问题呢?使用threadlcal - theadlocal的原理,弱引用,内存泄漏问题-remove方法。
22. springbean的作用域?
- Singleton(单例):在整个应用程序中,只存在一个 Bean 实例。每次请求都将返回相同的实例,这是 Spring 默认的 Bean 作用域。
- Prototype(原型):每次请求都将创建一个新的 Bean 实例。在应用程序中可以有多个 Bean 实例。
- Request(请求):每次 HTTP 请求都将创建一个新的 Bean 实例,该作用域仅适用于 Web 应用程序。
- Session(会话):每个 HTTP 会话都将创建一个新的 Bean 实例,该作用域同样只适用于 Web 应用程序。
23. Spring框架中使用了那些设计模式?谈一谈
- 工厂模式(创建bean),代理模式,责任链模式(拦截器 ),单例模式(spring默认的作用与就是单例的 也就是整个应用期间只会有一个相同的bean实例)模板方法(transactionTemplate类的使用)
24. SpringMVC中MVC分别代表什么?对应的是处理什么逻辑、功能?
- model模型:数据结构,实体类。view视图:用于展示数据,与用户交互(前端)。controller层:逻辑处理,用于模型和view之间的交互。
25. springMVC的处理请求的流程?
外部链接:https://www.yuque.com/hollis666/wty0im/kdhprf
26. SpringBoot?
- springboot是一个非常常用和重要的spring框架。它的特点是自动装配,内嵌了tomcat等web服务器,相对于spring简化了大量的xml配置文件。
27. SpringBoot的启动流程?
外部链接:https://www.yuque.com/hollis666/wty0im/fadkbgd4fyv8816p
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);也可简体七调用静态方法
- springboot的启动流程先从@@SpringBootApplication的启动类开始,执行SpringApplication.run方法。
- 执行的第一步是new springapplication进行初始化;第二步是执行run方法
- 其中初始化中调用了initialize方法主要做了这几件事:1.将我们的启动类加载到源中。2.设置我们的web环境。3.从spring.factories文件中加载初始化器(自动装配的核心)。4.同样从spring.factories中加载监听器springaplicationListnner。5.确定我们的主应用类。
- 第二步:执行run方法主要是开启计时器(统计运行时长),配置环境参数,准备上下文等。
public SpringApplication(Object... sources) {
initialize(sources);
}
private void initialize(Object[] sources) {
// 添加源: 如果 sources 不为空且长度大于 0,则将它们添加到应用的源列表中
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 设置 Web 环境: 推断并设置 Web 环境(例如,检查应用是否应该运行在 Web 环境中)
this.webEnvironment = deduceWebEnvironment();
// 加载初始化器: 设置 ApplicationContext 的初始化器,从'spring.factories'文件中加载ApplicationContextinitializer 实现
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextinitializer));
// 设置监听器: 从 'spring.factories' 文件中加载 ApplicationListener 实现
setListeners((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 确定主应用类: 通常是包含 main 方法的类
this.mainApplicationClass = deduceMainApplicationClass();
}
28. springboot是如何实现快速装配的?
- 项目启动会扫描@SpringAplication注解,它是一个复合注解,里面包含@componentScan和@configuration以及@EnableAutoconfiguration注解。其中@componentScan主要用于扫描@Component、@Service、@Repository、@Controller注解的类到容器中,@configuration是一个配置类。其中@EnableAutoconfiguration就是一个自动装配注解,里面最重要的就是@import注解,这个注解会将resouce下的spring.factories文件的类路径加载容器中,最后通过@conditinal注解进行bean的过滤加载。
29. SpringCloud?
30. 你们公司使用了那些组件?为什么使用,他的功能是什么?当前有那些实现方式,你们的架构筛选是什么?考虑的哪些方面?
31. Spring,SpringBoot、SpringMVC的区别?
- 总的来说,Spring Framework 是整个 Spring 生态系统的核心,提供了多种功能和模块;Spring Boot 则是用于简化 Spring 应用程序开发的工具;而 Spring MVC 是 Spring Framework 中用于构建 Web 应用程序的模块。
32. SpringBoot与SpringCloud的区别?
springboot是单体机,如果其中某个链路崩掉了就会影响整个服务。
33. 没有spring会如何进行web开发?
- jsp,Struts–Web应用程序框架
34. spring和tomcat的关系?
- tomcat是一个web服务器。spring是一个核心框架,用来构建应用程序。tomcat用来运行应用程序。
35. spring的线程池模型?
36. spring异步的实现?
- 文章推荐:https://www.yuque.com/hollis666/wty0im/naw927g44ywpxw4e
- 异步也就是在一个线程执行时,指定某个方法重新执行新的线程然后异步返回结果。
- spring中可以通过原生的@async注解。但是建议使用自定义的线程池实现异步执行。
自定义线程池:
@Configuration
@EnableAsync
public class AsyncExecutorConfig {
@Bean("registerSuccessExecutor")
public Executor registerSuccessExecutor() {
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("registerSuccessExecutor-%d")
.build();
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadFactory(namedThreadFactory);
executor.setCorePoolSize(100);
executor.setMaxPoolSize(200);
executor.setQueueCapacity(1024);
executor.setKeepAliveSeconds(0);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
}
使用:
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class RegisterEventListener {
@EventListener(RegisterSuccessEvent.class)
@Async("registerSuccessExecutor")
public void onApplicationEvent(RegisterSuccessEvent event) {
RegisterInfo registerInfo = (RegisterInfo) event.getSource();
// 执行发送欢迎短信的逻辑
// 这里注意要控制好并发,这个例子就不细说了,可以参考基于Redis的分布式锁来解决
}
}
37. 如何读取配置文件中的值? 三种
1. 使用 @Value 注解:通过在类成员变量上使用 @Value 注解,可以直接从配置文件中获取对应的属性值。
@Value("${myapp.propertyName}")
private String propertyValue;
2. 通过 Environment 对象:可以通过注入 Environment 对象来获取配置属性的值。
@Autowired
private Environment env;
String propertyValue = env.getProperty("myapp.propertyName");
3. 通过 @ConfigurationProperties 注解:通过创建一个 Java Bean 类,并使用 @ConfigurationProperties 注解将配置文件中的属性映射到该类的属性上,然后通过注入该 Bean 来获取配置属性的值。
@Configuration
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
private String propertyName;
// getter and setter
}
// 在其他类中注入该 Bean
@Autowired
private MyAppProperties myAppProperties;
38. 如何自定义一个starter?
面试百问
问答推荐:https://www.yuque.com/hollis666/wty0im/ugbv46
1. String str =new Sring(“1”)+new String(“2”);创建了多少个对象?
- jdk8创建了6个对象。堆中四个对象1,对象2 ,对象12以及new stringBuffer对象。方法区中包含常量1,常量2。
2. 说一下常见的集合吧?AarryList初始容量?如何进行扩容的?AarryList是线程安全的吗?
- collection下的list和set,map array
3. CAS是如何保证线程安全的? CAS会出现什么问题?哪些类的底层都是用到了CAS操作?
- Atomic类,ConcurrentHashMap(区别使用重量级锁的hashtable和CopyOnWriteArrayList),AQS队列同步器中实现类 ReentrantLock
4. volitile关键字有什么作用?1.防止指令重排序,2.保证共享变量的可见性
5. synchronized和ReentrantLock有什么区别?AQS在实现锁的公平性和非公平性有什么不同?synchronized锁升级的整个过程?
- mutex,aqs,公平和非公平锁,unlock主动释放。双向队列。但可能导致某些线程长时间无法获取锁的情况。
6. CopyOnWriteArrayList是怎么保证集合的安全性的?
- add时使用synchronized
7. CyclicBarrier和CountDownLatch的区别?
8. 如何实现三个线程顺序打印ABC?
- A.join方法 B.join 等待调用方先完成。
9. 线程池有哪些参数?新创建的线程池有核心线程吗?核心线程数如何设置?阻塞队列有哪些实现?拒绝策略有哪些?
- 常见的阻塞队列实现包括:
- 基于链表的无界队列
- 基于数组的有界队列。
- 拒绝策略:由调用该线程的对象执行新任务;直接丢弃;抛出异常;
10. 了解ThreadLocal吗?ThreadLocal底层实现?ThreadLocal使用过程存在什么问题,如何解决?
11. 为什么 ConcurrentHashMap 比 HashTable效率要高?
12. ConcurrentHashMap1.7和1.8分别是如何保证集合的安全性的?
13. 死锁发生的必要条件是什么?如何避免死锁问题?
14. 如何在Spring的启动过程中做缓存预热?
15. Java中的值的传递方式?
- java中的参数传递方式取决于参数类型,对于基本数据类型采用值传递,对于对象类型采用引用传递的副本
16. 八大基本数据类型?
1. 整型(Integer):
- byte:8 位,范围为 -128 到 127
- short:16 位,范围为 -32768 到 32767
- int:32 位,范围为 -2147483648 到 2147483647
- long:64 位,范围为 -9223372036854775808 到 9223372036854775807
2. 浮点型(Floating-Point):
- float:32 位,范围为 ±3.40282347E+38F(有效位数为6-7位)
- double:64 位,范围为 ±1.79769313486231570E+308(有效位数为15位)
3. 字符型(Character):
- char:16 位 Unicode 字符,范围为 '\u0000'(0)到 '\uffff'(65535)
4. 布尔型(Boolean):
- boolean:表示逻辑值,只有两个取值:true 和 false
评论区