Spring的主要核心是SpringIOC和SpringAOP。
SringIOC:反转控制,是类的实例化操作交给Spring去完成。
SpringAOP:切面编程,是动态代理技术的一种实现。
我的IOC:
1.每次我们写Controller的时候,我们都会在类上面使用注解Controller。
所以我们定一个注解:
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
public @interface Action {
}
2.定义注解后,我们再写一个Controller并写上注解。
@Action
public class TestAction {
public String test() {
return "123";
}
}
3.这样话,我们要的结果是用我们自己的扫描器去扫描注解了Action的的类,然后实列化。并且,在程序需要的时候能够获取到。
扫描注解类
public class PackageClassScan {
public static List<String> scan(String packageName){
String pacakgeNameTmp = packageName.replaceAll("\\.", "/") + "/";
//System.out.println(pacakgeNameTmp);
String rootPath = PackageClassScan.class.getResource("/").getPath();
System.out.println(rootPath);
File f = new File(rootPath + pacakgeNameTmp);
//System.out.println(f.getAbsolutePath());
String[] classFiles = f.list();
List<String> paths = new ArrayList<>();
for(String fclf: classFiles) {
//System.out.println(fclf);
if(!new File(fclf).isDirectory()) {
paths.add(fclf);
}
}
return paths;
}
}
- 通过scan方法,我们可以根据传入的package,扫描package下面的class。主要是为了获取package下面的类。
主要做实列化和存储对象的类
public class ApplicationContext {
Map<Class<?>,Object> beas = new HashMap<>();
public ApplicationContext(String packageName) {
List<String> clss = PackageClassScan.scan(packageName);
//判断是否有Action
for(String clsName: clss) {
try {
String clsNameTmp = clsName.replace(".class", "");
Class<?> cls = Class.forName(packageName + "." +clsNameTmp);
addActionBeans(cls);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void addActionBeans(Class<?> cls) throws InstantiationException, IllegalAccessException {
Action annotion = cls.getAnnotation(Action.class);
if(annotion != null)
addBeas(cls);
}
public <T> T getBeans(Class<T> cls){
return (T)beas.get(cls);
}
public void addBeans(Class<?> cls,Object o) {
beas.put(cls, o);
}
public void addBeas(Class<?> cls) throws InstantiationException, IllegalAccessException {
beas.put(cls, cls.newInstance());
}
}
- 构造方法,做了扫描package下面的类操作。
- addActionBeans方法判断了是否又Action注解。又注解就将该类实例化然后放置IOC容器中保存。
- getBeans在程序又需要的时候,通过该方法获取到对应的Bean,填充。
3.Main方法
public class App {
public static void main( String[] args ){
String packageName = "com.aming.novel.controller";
ApplicationContext context = new ApplicationContext(packageName);
System.out.println(context.getBeans(TestAction.class));
}
}
Spring的实现
1.Spring的扫描
我们可以随便新建一个包:com.aming.action
然后在包里面添加俩个类
写一个main方法开始跟踪看看Spring是怎么扫描的。
public static void main( String[] args )
{
ApplicationContext a = new AnnotationConfigApplicationContext(new String[] {"com.aming.action"});
}
- 首先
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
很明显是通过scan方法,我们再点进去
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
这里调用了 this.scanner.can(“com.aming.action”)
我们这里打下断点使用debug调试进入
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
这里调用了doScan方法,打断点继续调试
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
注意Set candidates = findCandidateComponents(basePackage);
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
跟着:Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
@Override
public Resource[] getResources(String locationPattern) throws IOException {
if (this.resourceLoader instanceof ResourcePatternResolver) {
return ((ResourcePatternResolver) this.resourceLoader).getResources(locationPattern);
}
return super.getResources(locationPattern);
}
接着:return super.getResources(locationPattern);
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
}
接着:return this.resourcePatternResolver.getResources(locationPattern);
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf(":") + 1;
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
接着:return findPathMatchingResources(locationPattern);
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<Resource>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
}
else if (isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[result.size()]);
}
注意:result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
throws IOException {
File rootDir;
try {
rootDir = rootDirResource.getFile().getAbsoluteFile();
}
catch (IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Cannot search for matching files underneath " + rootDirResource +
" because it does not correspond to a directory in the file system", ex);
}
return Collections.emptySet();
}
return doFindMatchingFileSystemResources(rootDir, subPattern);
}
```
注意:return doFindMatchingFileSystemResources(rootDir, subPattern);
```java
protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
}
Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
Set<Resource> result = new LinkedHashSet<Resource>(matchingFiles.size());
for (File file : matchingFiles) {
result.add(new FileSystemResource(file));
}
return result;
}
到这里就将我们那里com.aming.action下面的类包装成FileSystemResource返回。
然后我们回到findCandidateComponents